Authentication
Every Partner API request carries one org-scoped API key. This page covers what a key looks like, what each scope unlocks, how to send it, and how to read the three different ways a request can be turned away.
What a key is
A key is a single opaque string: the prefix e67_live_ followed by 32 random characters. There is no separate key id and secret, and no signing step on your side. Holding the string is the whole credential, which is why the rest of this page is mostly about not losing it.
e67_live_7Kq2Yb3dR1sVpN8xL4mZ0aC6tE5wJ9uH- It is shown once. The full key appears in the response that creates it and on the console screen that made the call. event67 stores only a hash of it, so we cannot show it to you again and neither can support. Losing it means creating a new one.
- It belongs to an organization, not to you. Keys survive the person who created them leaving. That is deliberate for a nightly job, and it is why the console records who created each key and shows when it was last used.
- The console shows a prefix and the last four characters, for example
e67_live_7Kq2Yb3d…uH. That is enough to recognise a key in a list and not enough to use one. - There are no test-mode keys. The
e67_test_prefix is reserved and nothing mints it today. If you need a safe place to experiment, make a draft event and point a key at that.
Scopes
A key carries between one and four scopes, chosen when you create it and fixed thereafter. Give a key the least it needs: a badge printer that only reads names has no business holding a key that can write attendees.
| Scope | What it unlocks |
|---|---|
| events:read | Events, sessions, tracks, rooms, speakers, sponsors and published info sections. The programme, in other words. |
| attendees:read | The attendee list for an event, and a single attendee by id. |
| attendees:write | Creating an attendee and updating an existing one. Creating an attendee this way does not email anybody; inviting stays a deliberate action in the console. |
| engagement:read | Check-ins, session attendance, forms and their submissions, and session feedback. This is the most personal data the API serves; scope it separately on purpose. |
GET /me is the one endpoint that needs no scope. Every other path names the scope it requires in the endpoint table, and a key without it gets a403 that names the scope it was missing.
Sending the key
The header event67 expects is Authorization: Bearer.
curl -s https://api.event67.com/partner/v1/me \
-H "Authorization: Bearer e67_live_7Kq2Yb3dR1sVpN8xL4mZ0aC6tE5wJ9uH"X-API-Key is accepted as a secondary form, for tooling that cannot set anAuthorization header. It is not preferred and it is not more secure; use it only when the first is genuinely unavailable.
curl -s https://api.event67.com/partner/v1/me \
-H "X-API-Key: e67_live_7Kq2Yb3dR1sVpN8xL4mZ0aC6tE5wJ9uH"Never put a key in a query string. URLs end up in browser history, proxy logs and referrer headers, and a key in any of those has to be treated as compromised.
When a request is refused
Three status codes mean three genuinely different problems, and each one carries adetails.reason your client can branch on. Every response body is the same envelope described under errors.
401, the key itself
The credential is missing, unrecognised, revoked or past its expiry. The response also carriesWWW-Authenticate. Naming which of the four it was leaks nothing, because guessing a 32-character random key is not a thing anybody can do.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="event67 partner API"
Content-Type: application/json
{
"error": {
"code": "UNAUTHORIZED",
"message": "This API key has been revoked.",
"details": { "reason": "revoked_key" },
"requestId": "b8d1f0c2-3a4e-4d67-9f01-5c2e7a8b3d90"
}
}| details.reason | What happened | What to do |
|---|---|---|
| missing_key | Neither header was sent. | Send one. Check for a proxy stripping Authorization. |
| invalid_key | No key matches that string. | Check for truncation or a stray newline in the secret store. |
| revoked_key | Somebody revoked it in the console. | Ask an org admin why, then issue a new key. |
| expired_key | The key had an expiry and it has passed. | Issue a new key. Expiry is set at creation and cannot be extended. |
402, the organization has not paid
The key is valid and the organization is not entitled to integrations. This is not a per-request condition and retrying will not clear it, so treat it as a stop and surface it to a human.
{
"error": {
"code": "PAYMENT_REQUIRED",
"message": "Integrations need a payment method on file.",
"details": { "reason": "payment_required" },
"requestId": "6f2b8d10-77ac-4e39-b154-90ce3a7d5f68"
}
}| details.reason | What happened | What to do |
|---|---|---|
| payment_required | No card on file and no active annual plan. | An org admin adds a payment method in the console. Adding one charges nothing. |
| billing_suspended | The account is suspended. | Contact event67 support. A card will not lift this. |
| billing_closed | The account is closed. | Contact event67 support. |
403, the key is not allowed to do that
Either the key lacks the scope the endpoint needs, or the organization itself is suspended. The scope case names the scope, so the message you log is actionable.
{
"error": {
"code": "FORBIDDEN",
"message": "This key does not have the attendees:write scope.",
"details": { "reason": "insufficient_scope", "requiredScope": "attendees:write" },
"requestId": "1c4a9e77-0b2d-4f83-a6e5-8d0b1f37c249"
}
}| details.reason | What happened | What to do |
|---|---|---|
| insufficient_scope | The endpoint needs a scope this key does not carry. details.requiredScopenames it. | Create a new key with that scope. Scopes cannot be added to an existing key. |
| org_suspended | The organization is not active. | Contact event67 support. |
One more code worth handling: 503 withcode: "INTEGRATIONS_DISABLED". That is the platform kill switch, it applies to everybody at once, and it is temporary. Back off and retry rather than alerting an operator.
Expiry, revocation and rotation
A key can be created with an expiry date or without one. A key with no expiry is the right choice for a long-running integration you monitor; a key with one is the right choice for a contractor, a one-off migration, or anything you would otherwise forget to clean up.
Revocation is immediate and permanent. A revoked key stays listed in the console with the date and the person who revoked it, because a key that vanishes from the list takes its audit trail with it. Revoking is idempotent: revoking an already-revoked key succeeds and changes nothing.
There is no rotate button, and that is deliberate: rotating in place would mean a window where neither the old nor the new key works. Rotate by overlap instead.
- Create a second key with the same scopes and a name that says which is which.
- Deploy it. Both keys work at once, so there is no cutover moment.
- Watch the old key's last-used timestamp in the console until it stops moving, which proves nothing is still holding it.
- Revoke the old key.
An organization may hold up to 100 active keys. If you are near that, the answer is usually fewer integrations holding more scopes, not more keys.
Keeping a key secret
- Server side only. A key in a browser bundle, a mobile app or a public repository is a key that reads your whole attendee list. There is no origin restriction and no referrer check that would save you, because the key is the entire credential.
- Store it where you store database passwords. A secret manager, not an environment file in a shared drive and not a message to a colleague.
- Scan for the prefix.
e67_live_is a stable, distinctive literal. Add it to your secret-scanning rules and to your pre-commit hooks, and you will catch a leaked key before it is pushed rather than after. - One key per integration. Sharing a key across three jobs means revoking it breaks three jobs, and the last-used timestamp tells you nothing about which of them is still alive.
- If a key leaks, revoke first and investigate second. Revocation costs one click and creating a replacement costs another.
Everything the API will and will not tell a key holder is set out in theconventions and endpoint reference.