Connect
Six steps from a new app to a verified webhook. See Authentication for the OAuth2 details.
1. Register an app
Register an app in the developer portal. A sandbox app works at once, against sandbox companies only.
A production app is available after the partner review. You note the client ID, and for a confidential client the secret, which is shown once.
You should see: A client ID, and the redirect URIs and scopes you registered.
2. Send a company to Connect
Put a Connect Sparro button in your app. It opens the Connect URL below with your client ID in the path. The person signs in or creates an account, picks a company and approves the scopes you ask for. Only an owner of the company can approve.
Public clients must send a PKCE code_challenge with code_challenge_method=S256. Any other query parameter is ignored.
The browser comes back to your redirect_uri with a code and your state. Check the state before you use the code.
https://app.sparro.dk/connect/<client_id>
?redirect_uri=https://app.example.com/sparro/callback
&scope=shifts:read shifts:write
&state=<random>
&code_challenge=<pkce>
&code_challenge_method=S256You should see: Your redirect_uri receives ?code=…&state=… after the owner approves.
3. Exchange the code for tokens
Post the code to the token endpoint from your server. For a public client, send the code_verifier that matches your code_challenge instead of a secret.
curl -X POST "https://app.sparro.dk/api/v2/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET&code=$CODE&redirect_uri=$REDIRECT_URI"You should see: An access_token, a refresh_token and the scope that was granted. The access token lasts one hour.
4. Read with the access token
Call a read endpoint with the token as a bearer credential. The shifts list also gives you employee_ref values: an employee is only ever an opaque reference, and it is different for every app.
An employee with no shift or mileage record yet has no reference to read.
curl -X GET "https://app.sparro.dk/api/v2/shifts?limit=100" \
-H "Authorization: Bearer $SPARRO_ACCESS_TOKEN"You should see: A shifts array and a next_cursor. Each shift has an employee_ref.
5. Write with an Idempotency-Key
Every write needs an Idempotency-Key header. Send a new random UUID for each logical operation and reuse it only to retry that operation.
Send the same request again with the same key and you get the first response back with Idempotent-Replayed: true, and no second record.
curl -X POST "https://app.sparro.dk/api/v2/shifts" \
-H "Authorization: Bearer $SPARRO_ACCESS_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"employee_ref":"emp_Qm9hcmRFeGFtcGxlUmVmZXJlbmNlS2V5Rm9yRG9jc09","start_time":"2026-11-02T08:00:00+01:00","end_time":"2026-11-02T16:00:00+01:00","break_minutes":30}'You should see: 201 with the new shift. The repeat answers 201 with the same body and Idempotent-Replayed: true.
6. Verify a webhook signature
Create an endpoint in the developer portal and keep its signing secret on your server. Every delivery carries a Sparro-Signature header. Check it against the test vector on the Webhooks page before you go live.
t, v1 = parse(request.headers["Sparro-Signature"])
signed = t + "." + request.raw_body
expect = hex(HMAC_SHA256(signing_secret, signed))
valid = constant_time_equal(expect, v1)
and abs(now() - t) <= 300You should see: Your verifier accepts the test vector and rejects the same body with one character changed.