Hoppa till innehållet

Account

Keys

Every request carries an API key as a bearer token. There are two kinds, and an operation takes only one of them.

Two kinds of key

Server keyAccount key
Starts withsk_, or sk_test_ on a test serverak_
Belongs toOne serverThe account
DoesSends, reads the message log, and manages the server’s streams, suppressions, webhooks and its own keys. An SMTP client signs in with it.Creates, renames and deletes servers, manages every server’s keys, and manages the sending domains.
NeverManages the account’s servers or domainsSends or reads mail
MadeWith its server, or by the server’s own key or the account keyBy an administrator in the dashboard, under Account › API keys. The API cannot make one.

Your application holds a server key. Keys have no narrower scopes: every key of a server can do all that a server key does. A key of the wrong kind fails with 403 wrong_token_kind, and the message names the kind the operation needs.

The SDK has a client for each: Sendora takes a server key, and SendoraAccount an account key. Keep both in the environment on the server, as SENDORA_API_TOKEN and SENDORA_ACCOUNT_TOKEN. Never ship either to a browser.

The SDK has a client for each: Sendora takes a server key, and SendoraAccount an account key. Keep both in the environment on the server, as SENDORA_API_TOKEN and SENDORA_ACCOUNT_TOKEN. Each client reads its own variable when you give it no key.

A server comes with its first key

A new server comes with a default transactional stream and its first key, whether you create it in the dashboard or with the account key. The response shows the key’s value, and only this once:

POST /v1/servers
curl -X POST https://api.sendora.se/v1/servers \
  -H "Authorization: Bearer $SENDORA_ACCOUNT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Invoicing"
}'
import { SendoraAccount } from '@sendora/sdk';

const account = new SendoraAccount({
  token: process.env.SENDORA_ACCOUNT_TOKEN,
});

const server = await account.servers.create({
  name: 'Invoicing',
});

console.log(server.serverId);
console.log(server.token.token);
import os

from sendora import SendoraAccount

account = SendoraAccount(
    os.environ["SENDORA_ACCOUNT_TOKEN"]
)

server = account.servers.create(name="Invoicing")

print(server.server_id)
print(server.token.token)

Response

{
  "serverId": "7c1e4d2a-0b9f-4a3e-8d6c-5e2f1a9b8c70",
  "name": "Invoicing",
  "mode": "live",
  "createdAt": "2026-09-20T12:00:00.000Z",
  "token": {
    "tokenId": "3b0c9e2f-6d4a-4e8b-8a1c-9f2d7e6c5b40",
    "name": "default",
    "prefix": "sk_a1b2c3d4e5",
    "createdAt": "2026-09-20T12:00:00.000Z",
    "revokedAt": null,
    "token": "sk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v"
  }
}

Store token.token in your secret store. It is never shown again.

Create and list keys

A server holds at most two live keys: the one in use and the one being rotated in. The account holds at most two live account keys the same way. A third fails with 409 token_limit. A key’s name only helps people tell the keys apart.

Create a key:

POST /v1/servers/{serverId}/tokens
curl -X POST https://api.sendora.se/v1/servers/{serverId}/tokens \
  -H "Authorization: Bearer $SENDORA_ACCOUNT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Invoicing system"
}'
import { SendoraAccount } from '@sendora/sdk';

const account = new SendoraAccount({
  token: process.env.SENDORA_ACCOUNT_TOKEN,
});

const key = await account.servers.tokens.create(
  '7c1e4d2a-0b9f-4a3e-8d6c-5e2f1a9b8c70',
  { name: 'Invoicing system' },
);

console.log(key.token);
import os

from sendora import SendoraAccount

account = SendoraAccount(
    os.environ["SENDORA_ACCOUNT_TOKEN"]
)

key = account.servers.tokens.create(
    "7c1e4d2a-0b9f-4a3e-8d6c-5e2f1a9b8c70",
    name="Invoicing system",
)

print(key.token)

Response

{
  "tokenId": "9f2d7e6c-5b40-4e8b-8a1c-3b0c9e2f6d4a",
  "name": "Invoicing system",
  "prefix": "sk_c3d4e5f6g7",
  "createdAt": "2026-09-20T12:05:00.000Z",
  "revokedAt": null,
  "token": "sk_c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x"
}

List a server’s keys:

GET /v1/servers/{serverId}/tokens
curl https://api.sendora.se/v1/servers/{serverId}/tokens \
  -H "Authorization: Bearer $SENDORA_ACCOUNT_TOKEN"
import { SendoraAccount } from '@sendora/sdk';

const account = new SendoraAccount({
  token: process.env.SENDORA_ACCOUNT_TOKEN,
});

const { tokens } = await account.servers.tokens.list(
  '7c1e4d2a-0b9f-4a3e-8d6c-5e2f1a9b8c70',
);

for (const key of tokens) {
  console.log(
    key.name,
    key.prefix,
    key.revokedAt ? 'revoked' : 'live',
  );
}
import os

from sendora import SendoraAccount

account = SendoraAccount(
    os.environ["SENDORA_ACCOUNT_TOKEN"]
)

listed = account.servers.tokens.list(
    "7c1e4d2a-0b9f-4a3e-8d6c-5e2f1a9b8c70"
)

for key in listed.tokens:
    print(
        key.name,
        key.prefix,
        "revoked" if key.revoked_at else "live",
    )
Response (18 lines)
{
  "tokens": [
    {
      "tokenId": "3b0c9e2f-6d4a-4e8b-8a1c-9f2d7e6c5b40",
      "name": "default",
      "prefix": "sk_a1b2c3d4e5",
      "createdAt": "2026-09-20T12:00:00.000Z",
      "revokedAt": null
    },
    {
      "tokenId": "9f2d7e6c-5b40-4e8b-8a1c-3b0c9e2f6d4a",
      "name": "Invoicing system",
      "prefix": "sk_c3d4e5f6g7",
      "createdAt": "2026-09-20T12:05:00.000Z",
      "revokedAt": null
    }
  ]
}

These samples use the account key. A server key manages its own server’s keys the same way:

KeyManages the keys ofPath
Server keyIts own server/v1/tokens
Account keyAny server of the account/v1/servers/{serverId}/tokens

With a server key, sendora.tokens.create({ name }), sendora.tokens.list() and sendora.tokens.revoke(tokenId) manage the server’s own keys.

With a server key, sendora.tokens.create(name=...), sendora.tokens.list() and sendora.tokens.revoke(token_id) manage the server’s own keys.

Rotation is create, switch, revoke

  1. Create the new key.
  2. Move your systems to it; both keys work meanwhile.
  3. Revoke the old one. It stops working at once.
DELETE /v1/servers/{serverId}/tokens/{tokenId}
curl -X DELETE https://api.sendora.se/v1/servers/{serverId}/tokens/{tokenId} \
  -H "Authorization: Bearer $SENDORA_ACCOUNT_TOKEN"
import { SendoraAccount } from '@sendora/sdk';

const account = new SendoraAccount({
  token: process.env.SENDORA_ACCOUNT_TOKEN,
});

await account.servers.tokens.revoke(
  '7c1e4d2a-0b9f-4a3e-8d6c-5e2f1a9b8c70',
  '3b0c9e2f-6d4a-4e8b-8a1c-9f2d7e6c5b40',
);
import os

from sendora import SendoraAccount

account = SendoraAccount(
    os.environ["SENDORA_ACCOUNT_TOKEN"]
)

account.servers.tokens.revoke(
    "7c1e4d2a-0b9f-4a3e-8d6c-5e2f1a9b8c70",
    "3b0c9e2f-6d4a-4e8b-8a1c-9f2d7e6c5b40",
)

The last live key of a server or of the account cannot be revoked (409 last_token), so nothing is ever locked out by mistake. A revoked key stays in the list with its revokedAt, so the history is visible.

If a key leaks, rotate it at once. You cannot revoke a server’s only key, so create the new key first. For an account key, an administrator does the same under Account › API keys. Keys are stored hashed and never shown again, so revoke any key you cannot account for.