Skip to content
Home

Management API

Status: Draft Last Updated: 2026-06-15 Audience: customer

This is the reference for the management API — a server-to-server, account-wide view of who has connected to your product and what they have granted. It answers “which of my end users have active grants, and when did each source last sync?” without you holding or iterating every end user’s token.

The management API is distinct from the Data API: the data API reads one end user’s content with an end-user token (cct_…); the management API reads grant metadata across your whole account with a management token (ldb_…). It never returns message bodies, contacts, or any personal content.

All example values are synthetic. Replace them with your own.

When to use it

  • Build an internal dashboard of connected users and their sync health.
  • Detect users who have disconnected (gone dark) so you can prompt them to reconnect.
  • Confirm a specific user’s current source connections from your backend.
  • Drive periodic reconciliation; pair it with webhooks for real-time updates.

Endpoint and authentication

  • Endpoint: POST /graphql/v1 — the same endpoint as the data API. The token tier you present determines which queries you may run.
  • Authentication: a management token, obtained via grant_type=client_credentials.
Terminal window
curl -X POST https://api.example-unbound.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=a1b2c3d4" \
-d "client_secret=ldb_syn_clientsecret_REPLACE_ME"
{
"access_token": "ldb_syn_7h3k9m2p_X4f8Qa2bN6cR1dS5tU9vW0xY3zZ",
"token_type": "Bearer",
"expires_in": 3600
}

Management tokens always expire after one hour. Re-mint as needed; there is nothing to revoke. Send it as Authorization: Bearer ldb_…. The full token reference is in Authentication.

A management token only unlocks the two management queries below. Presenting any other token tier to these queries returns a GraphQL error with extensions.code = "UNAUTHENTICATED". Conversely, an end-user token cannot run these queries, and a management token cannot run the data queries.

authorizedEndUsers

Lists every end user under your account who currently has at least one active grant. Fully-revoked users do not appear — absence from this list means “no active grants.”

Terminal window
curl -X POST https://api.example-unbound.com/graphql/v1 \
-H "Authorization: Bearer ldb_syn_7h3k9m2p_X4f8Qa2bN6cR1dS5tU9vW0xY3zZ" \
-H "Content-Type: application/json" \
-d '{"query": "{ authorizedEndUsers(first: 50) { totalCount edges { cursor node { endUserID hasActiveGrant lastAuthorizedAt activeGrants { source grantedAt lastSyncedAt } } } pageInfo { hasNextPage endCursor } } }"}'
{
"data": {
"authorizedEndUsers": {
"totalCount": 2,
"edges": [
{
"cursor": "c3Vu...",
"node": {
"endUserID": "user-42",
"hasActiveGrant": true,
"lastAuthorizedAt": "2026-06-13T17:05:12Z",
"activeGrants": [
{ "source": "gmail", "grantedAt": "2026-06-13T17:04:05Z", "lastSyncedAt": "2026-06-13T17:09:31Z" },
{ "source": "imessage", "grantedAt": "2026-06-13T17:05:12Z", "lastSyncedAt": null }
]
}
},
{
"cursor": "c3Vv...",
"node": {
"endUserID": "user-77",
"hasActiveGrant": true,
"lastAuthorizedAt": "2026-06-10T08:21:00Z",
"activeGrants": [
{ "source": "gmail", "grantedAt": "2026-06-10T08:20:10Z", "lastSyncedAt": "2026-06-13T06:00:00Z" }
]
}
}
],
"pageInfo": { "hasNextPage": false, "endCursor": "c3Vv..." }
}
}
}

AuthorizedEndUser fields:

FieldTypeMeaning
endUserIDString!Your identifier for this user — the opaque value you passed to /authorize. This is how you correlate to your own records.
activeGrants[CustomerGrant!]!The user’s active grants (see below). Never includes revoked grants.
lastAuthorizedAtDateTime!When the user most recently completed authorization.
hasActiveGrantBoolean!Always true for users returned here (they qualify by having an active grant).

CustomerGrant fields:

FieldTypeMeaning
sourceString!The source identifier — see Data Sources.
grantedAtDateTime!When this grant was first issued.
lastSyncedAtDateTimeWhen the most recent import for this source completed. Null until the first import finishes after the grant is authorized.

lastSyncedAt is the key operational signal: a grant that is active but has lastSyncedAt: null is connected but not yet synced — typical right after authorization, especially for sources whose first import runs asynchronously (see Data Sources). Once the import completes, lastSyncedAt becomes a timestamp.

Pagination

authorizedEndUsers is a standard cursor connection: first: N (default 25, max 100) and after: <cursor>. Loop until pageInfo.hasNextPage is false, feeding each endCursor into the next after. totalCount is nullable — do not depend on it.

endUserGrants

Look up one specific end user by your identifier for them. Returns an AuthorizedEndUser, or null.

Terminal window
curl -X POST https://api.example-unbound.com/graphql/v1 \
-H "Authorization: Bearer ldb_syn_7h3k9m2p_X4f8Qa2bN6cR1dS5tU9vW0xY3zZ" \
-H "Content-Type: application/json" \
-d '{"query": "{ endUserGrants(endUserID: \"user-42\") { endUserID hasActiveGrant activeGrants { source grantedAt lastSyncedAt } } }"}'
{
"data": {
"endUserGrants": {
"endUserID": "user-42",
"hasActiveGrant": true,
"activeGrants": [
{ "source": "gmail", "grantedAt": "2026-06-13T17:04:05Z", "lastSyncedAt": "2026-06-13T17:09:31Z" },
{ "source": "imessage", "grantedAt": "2026-06-13T17:05:12Z", "lastSyncedAt": null }
]
}
}
}

A null result is not an error — it means the user is unknown to Unbound or has revoked all access:

{ "data": { "endUserGrants": null } }

null covers both “never authorized” and “authorized once, then revoked everything.” If you need to distinguish them, you must track authorization attempts in your own system; the management API only reports active state.

Operational patterns

“How many users have connected?” Read authorizedEndUsers.totalCount, or count edges as you page. Remember this counts only users with at least one active grant.

“Which of my users have disconnected?” Compare your own user list with the endUserIDs in authorizedEndUsers. Any user you know about who is absent from the list has no active grants — they revoked or never connected.

“Does user X currently have Gmail connected?”

{ endUserGrants(endUserID: "user-77") { activeGrants { source } } }

Check the returned source list for "gmail". A null result means user X has nothing connected.

“Is user X’s data fresh?” Read lastSyncedAt on the relevant grant. null means the first import has not completed yet; an old timestamp may indicate a stalled source worth re-checking.

“Build a health dashboard.” Poll authorizedEndUsers on a schedule (e.g. hourly) to refresh the full picture, and subscribe to webhooks for real-time grant and sync transitions in between polls. The management API is the pull channel; webhooks are the push channel — use both.

Relationship to the other surfaces

NeedUse
Read one user’s actual content (messages, people, events)Data API with that user’s cct_ token
Account-wide grant and sync metadata (no content)This guide — management API with an ldb_ token
Real-time notification of grant/sync transitionsWebhooks

Error reference

ConditionResult
Non-management token presentedGraphQL error, extensions.code = "UNAUTHENTICATED".
Management token expiredHTTP 401 with code: "AUTH_EXPIRED_KEY" (re-mint). See Authentication.
Unknown or fully-revoked endUserIDendUserGrants returns null (not an error).
  • Authentication — Minting and using the management token; the one-hour expiry and error codes.
  • Data API — The per-end-user content surface this complements.
  • Webhooks — The push counterpart for real-time grant and sync state.
  • Data Sources — The source identifiers reported in activeGrants.
  • Quickstart — Where authorizedEndUsers first appears in the onboarding walkthrough.