Authentication
Every request except a few public ones (/up, /v1/branding, the token endpoints and image redirects) needs an Authorization header. The API accepts two schemes:
Authorization: JWT <token> # user, admin and service JWTsAuthorization: Bearer <api key> # application API keysAnything else gets 401 Unauthorized with an empty JSON body.
Choose a credential
Section titled “Choose a credential”| Credential | Who uses it | Scheme | Scope |
|---|---|---|---|
| User JWT | Mobile and web apps acting for a signed-in person | JWT |
That user. Admin users can also administer every audience. |
| Application API key | Your server, acting as an app of one audience | Bearer |
One audience’s GraphQL endpoint. |
| Service JWT | Workers and automation | JWT |
One audience, limited to the token’s scopes. |
User JWTs (Facebook sign-in)
Section titled “User JWTs (Facebook sign-in)”Apps sign people in with Facebook, then exchange the Facebook token for an AudienceKit JWT:
POST /v1/tokenContent-Type: application/json
{ "facebook_token": "<Facebook user access token>", "host": "admin.hotmess.social", "device": { "identifier": "<stable device identifier>", "type": "apple", "model": "iPhone16,1", "version": "1.0", "build": "42" }}host is optional. On an audience’s host (admin.<audience domain>) the Facebook token must come from that audience’s Facebook app; otherwise it must come from the AudienceKit app. GET /v1/branding?host=… returns the app ID to use as facebook_app_id.
The API exchanges the token for a long-lived one, creates or updates the user from their Facebook profile, records the device and a new session, and returns:
{ "token": "<jwt>", "access_token": "<jwt>", "refresh_token": "<jwt>", "user": { "id": "…", "name": "…" }}Send it as Authorization: JWT <jwt>. The JWT is signed with HS256. Its claims are built at sign-in by the transforms in api/app/services/login_claims/: the user’s id and app-scoped fb_id, a jti that identifies the session, a role of user or admin, fb_app_id (the Facebook app used), and, for a sign-in on an audience’s host, audience_id. A JWT with audience_id only works on that audience’s endpoint.
A user JWT works only while its session exists. Sessions end when someone removes one of our Facebook apps: every app’s Deauthorize callback is POST /v1/facebook/deauthorize, which signs that Facebook user out everywhere and forgets their Facebook token. Their data stays until they ask Facebook to delete it, which calls POST /v1/facebook/data_deletion. Both verify Facebook’s signed_request against the AudienceKit app and every audience’s apps.
To register a device for push notifications, call POST /v1/token/device with device_type, vendor_identifier and notification_token.
Admin access
Section titled “Admin access”An admin is a user whose isAdmin flag is set; their JWTs carry role: admin. Admins can:
- with a platform JWT (no
audience_id), callPOST /v1/tenants/graphqlto list and create audiences and manage their domains, - run every GraphQL mutation (mutations refuse anyone else with “Administrator access required”),
- call the research endpoints for any audience, and hold every service scope.
Application API keys
Section titled “Application API keys”An application belongs to one audience and has an api_key. Send it as a bearer token:
curl https://api.audiencekit.io/v1/audience/$AUDIENCE_ID/graphql \ -H "Authorization: Bearer $AUDIENCE_KIT_API_KEY" \ -H "Content-Type: application/json" \ -d '{"query":"{ venues { id page { name } } }"}'A key only works on its own audience’s endpoint; any other audience returns 403 Forbidden. Requests made with a key have no current user, so me returns null.
Service tokens
Section titled “Service tokens”Machines that keep an audience’s data fresh use service tokens. A service token is a secret shaped like akst_<prefix>_<secret> that belongs to one audience and carries scopes. Exchange it for a one-hour JWT:
POST /v1/token/serviceContent-Type: application/json
{ "service_token": "akst_…" }{ "token": "<jwt>", "token_type": "JWT", "expires_at": "2026-10-06T06:00:00Z", "audience_id": "…", "audience": "LGBT", "scopes": ["research:read", "places:write", "events:write"]}The API checks on every request that the token behind a service JWT hasn’t been revoked or expired. An unknown, revoked or expired secret returns 401 with {"error": "invalid_service_token"}, and a missing scope returns 403 with {"error": "insufficient_scope", "required": "…"}.
| Scope | Allows |
|---|---|
research:read |
GET /v1/research/snapshot and GET /v1/research/batches |
places:write |
Upserting venues through POST /v1/research/batch (needs events:write too) |
events:write |
Upserting events through POST /v1/research/batch (needs places:write too) |
See Automate with service tokens to create and manage them.