Skip to content

GET /api/public/v1 ​

Answers the three questions a first-time or failing integration actually has: did the key authenticate, which organization does it speak for, and which scopes did it end up with.

Make this your first call. It is also the cheapest health check you can run against the API.

PropertyValue
MethodGET
Path/api/public/v1
ScopesNone
Cost1 token
BodyNone

No scopes required, deliberately ​

This is the one route that declares no scopes and is correct to. It reads nothing belonging to the organization beyond the key's own identity — which the caller is already holding.

That property is what makes it useful for debugging: a 200 here proves authentication, tenant resolution, rate limiting and the response envelope are all working, without depending on any resource route or on your organization having any data in it.

Any other route that touched organization data and declared no scopes would be a bug, since it would accept the narrowest key in existence.

Request ​

bash
curl -sS https://<your-app-domain>/api/public/v1 \
  -H "authorization: Bearer $CALL_CENTER_API_KEY" \
  -H "x-request-id: $(uuidgen)"

Response ​

200 OK

json
{
  "data": {
    "version": "v1",
    "organization_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "key": {
      "id": "3f5e7c19-4a2b-4d68-b0e3-7c1a9f2d5b48",
      "name": "Acme CRM sync"
    },
    "scopes": ["contact:read", "contact:write"]
  }
}
FieldTypeNotes
versionstringThe serving API version, matching x-api-version.
organization_idstring (uuid)The tenant this key acts as. Not overridable.
key.idstringIdentifies the credential. Useful for confirming a rotation.
key.namestring | nullThe name given at creation. null if it was created unnamed.
scopesstring[]See below.

Reading scopes ​

The list is filtered against the scopes this build recognises, so a key still carrying a grant we have since retired is shown what it can use today rather than what it was minted with. Ordering follows the canonical scope order, not the order the key was created with.

Treat this as authoritative over your own records. If you expected campaign:write and it is not in this list, the key does not have it — mint a new key rather than debugging the endpoint that rejected you.

Failure cases ​

SituationStatusCode
No key, or a malformed header401UNAUTHORIZED
Unrecognised key401UNAUTHORIZED
The key's organization was deleted401UNAUTHORIZED
Too many failed attempts from one IP429TOO_MANY_REQUESTS

There is no 403 here — with no scopes required, a key that authenticates is a key that is allowed.

Using it as a health check ​

Poll it at most once a minute. It costs a token from the same 120-per-minute budget as everything else, and a health check that consumes your import allowance is worse than no health check.

Source: src/routes/api/public/v1/+server.ts.