Scopes
Each API key holds a set of Scopes, chosen under What this key may do when you create it. Every route needs exactly one Scope, and a key without it is refused.
| Scope | In the app | Lets the key |
|---|---|---|
events:write | Write events | Record Events against Contacts and organizations that already exist. |
contacts:write | Write contacts | Create and update Contacts by external ID or email address. |
contacts:read | Read contacts | Look up a single Contact and read the fields stored on it. |
contacts:export | Export contact data | Download everything stored about one Contact, for a GDPR Article 15 request. |
contacts:erase | Erase contacts | Erase a Contact's personal data, for a GDPR Article 17 request. |
Each route in the API reference names the Scope it needs.
Why a new key can only write
A new key starts with events:write and contacts:write and nothing else. That is all a typical integration needs: it tells Throughline what happened and who it happened to, and never reads anything back.
A key that can only write is a small loss if it leaks. Whoever holds it can add noise, but cannot read your Contacts. A key that can read is a different matter: external IDs are usually sequential, so a leaked key with contacts:read can walk your whole contact database one ID at a time.
So grant the other three one at a time, and only to the tooling that needs them:
contacts:readto a service that has to look a Contact up.contacts:exportto whatever answers data access requests. It returns a person's complete personal data.contacts:eraseto whatever answers deletion requests. Erasure cannot be undone.
When a Scope is missing
The request is refused with 403 and the code insufficient_scope, and the message names the Scope the key lacks:
{ "code": "insufficient_scope", "message": "This API key is missing the contacts:read scope." }
Do not retry. A key's Scopes are fixed when it is created, so the fix is a new key with the Scope added; see Rotate and revoke keys.