API keys
Your API key lets your backend call the TEDR Pay API directly, without a browser session. The privateKey identifies the key, the secretKey signs your requests, and the webhookSecret signs the webhooks TEDR Pay sends you.
The five endpoints on this page manage the key itself and use your dashboard session (JWT) — they don't accept a key signature. Signing your own API requests with secretKey is covered on the Authentication page.
Key fields
Every API key has these fields. Secrets are only included in the response body when they're generated, not on every read.
| Field | Type | Returned | Description |
|---|---|---|---|
| id | string (UUID) | Always | Row identifier; used as :id in the update, rotate, and revoke paths. |
| name | string | Always | A label you choose, up to 50 characters. |
| privateKey | string | Always | Example: priv_4b870aa6-1f20-4889-b666-b16d33dd65e6 — identifies the key; not a secret. |
| secretKey | string | On create and rotate only | Example: sec_kTn0aVQ7l9pS2xY4uZ8bC1dE3fG5hJ7kL9mN0pQ2rS4 — signs your requests. |
| webhookSecret | string | On create and rotate only | Example: whsec_Xy2Zb4Nc6Vd8Ef0Gh2Ij4Kl6Mn8Op0Qr2St4Uv6Wx8 — signs the webhooks TEDR Pay sends you. |
| webhookUrl | string | null | Always | Where to send webhook notifications. See the webhooks reference for its URL requirements. |
| rotatedAt | datetime | null | Always | When the key was last rotated; null if it never has been. |
| revokedAt | datetime | null | Never | Internal revoke marker; not included in API responses. |
| createdAt | datetime | Always | When the key was issued. |
Rules
- Only one active key at a time. Revoke the current key before you can issue a new one.
- Rotating replaces
privateKey,secretKey, andwebhookSecretall at once.id,name,webhookUrl, andcreatedAtstay the same.
Secrets are shown once.
secretKey and webhookSecret are only returned in the create and rotate responses. Store them immediately — there is no way to fetch them again.
Revoking or rotating breaks the current integration.
A revoked key can no longer authenticate new requests. Rotating replaces the material immediately too — the previous secretKey and webhookSecret stop working the moment you rotate, so update your integration first.
All five endpoints require a valid session; requests without one get 401 UNAUTHORIZED.
Create an API key
/merchants/api-keysAuth: Session JWT only — how to get a JWT.
Issues the merchant’s active API key. Fails if one already exists — revoke it first.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| name | string | Required | A label for the key, up to 50 characters. | |
| webhookUrl | string | Optional | Where to send webhook notifications for this key. See the webhooks reference for its URL requirements. |
/merchants/api-keyscurl -X POST 'https://dev-app.tedr.com/api/merchants/api-keys' \
-H 'Authorization: Bearer YOUR_SESSION_JWT' \
-H 'Content-Type: application/json' \
-d '{"name": "Production Store", "webhookUrl": "https://myshop.com/api/webhooks/tedr"}'
const response = await fetch('https://dev-app.tedr.com/api/merchants/api-keys', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_SESSION_JWT',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Production Store',
webhookUrl: 'https://myshop.com/api/webhooks/tedr',
}),
});
const { id, privateKey, secretKey, webhookSecret } = await response.json();
import requests
response = requests.post(
'https://dev-app.tedr.com/api/merchants/api-keys',
headers={'Authorization': 'Bearer YOUR_SESSION_JWT'},
json={
'name': 'Production Store',
'webhookUrl': 'https://myshop.com/api/webhooks/tedr',
},
)
key = response.json()
Response: 201
{
"id": "b923bb15-aeb3-493a-a06f-8babc0b92fef",
"name": "Production Store",
"privateKey": "priv_4b870aa6-1f20-4889-b666-b16d33dd65e6",
"secretKey": "sec_kTn0aVQ7l9pS2xY4uZ8bC1dE3fG5hJ7kL9mN0pQ2rS4",
"webhookSecret": "whsec_Xy2Zb4Nc6Vd8Ef0Gh2Ij4Kl6Mn8Op0Qr2St4Uv6Wx8",
"webhookUrl": "https://myshop.com/api/webhooks/tedr",
"rotatedAt": null,
"createdAt": "2026-07-31T12:52:23.000Z"
}
| Code | Status | When |
|---|---|---|
| API_KEY_LIMIT_REACHED | 403 | This merchant already has an active key. |
| VALIDATION_ERROR | 400 | name is empty or longer than 50 characters, or webhookUrl is invalid. |
Get the active API key
/merchants/api-keysAuth: Session JWT only — how to get a JWT.
Returns the merchant’s active API key, without secrets.
/merchants/api-keyscurl 'https://dev-app.tedr.com/api/merchants/api-keys' \
-H 'Authorization: Bearer YOUR_SESSION_JWT'
const response = await fetch('https://dev-app.tedr.com/api/merchants/api-keys', {
headers: { Authorization: 'Bearer YOUR_SESSION_JWT' },
});
const { apiKey } = await response.json();
import requests
response = requests.get(
'https://dev-app.tedr.com/api/merchants/api-keys',
headers={'Authorization': 'Bearer YOUR_SESSION_JWT'},
)
api_key = response.json()['apiKey']
Response: 200
{
"apiKey": {
"id": "b923bb15-aeb3-493a-a06f-8babc0b92fef",
"name": "Production Store",
"privateKey": "priv_4b870aa6-1f20-4889-b666-b16d33dd65e6",
"webhookUrl": "https://myshop.com/api/webhooks/tedr",
"rotatedAt": null,
"createdAt": "2026-07-31T12:52:23.000Z"
}
}
If there is no active key, the response is { "apiKey": null } — this is a normal state, not an error.
Update an API key
/merchants/api-keys/:idAuth: Session JWT only — how to get a JWT.
Renames the key and/or changes its webhook URL. Key material is not affected.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| id | string (UUID) | Required | The key's id, from the create, rotate, or list response. |
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| name | string | Optional | New label for the key. Omit to leave it unchanged. | |
| webhookUrl | string | null | Optional | New webhook URL. Send null to clear it, or omit to leave it unchanged. See the webhooks reference for its URL requirements. |
/merchants/api-keys/:idcurl -X PATCH 'https://dev-app.tedr.com/api/merchants/api-keys/b923bb15-aeb3-493a-a06f-8babc0b92fef' \
-H 'Authorization: Bearer YOUR_SESSION_JWT' \
-H 'Content-Type: application/json' \
-d '{"webhookUrl": "https://myshop.com/api/webhooks/tedr-v2"}'
const response = await fetch(
'https://dev-app.tedr.com/api/merchants/api-keys/b923bb15-aeb3-493a-a06f-8babc0b92fef',
{
method: 'PATCH',
headers: {
Authorization: 'Bearer YOUR_SESSION_JWT',
'Content-Type': 'application/json',
},
body: JSON.stringify({ webhookUrl: 'https://myshop.com/api/webhooks/tedr-v2' }),
},
);
const { apiKey } = await response.json();
import requests
response = requests.patch(
'https://dev-app.tedr.com/api/merchants/api-keys/b923bb15-aeb3-493a-a06f-8babc0b92fef',
headers={'Authorization': 'Bearer YOUR_SESSION_JWT'},
json={'webhookUrl': 'https://myshop.com/api/webhooks/tedr-v2'},
)
api_key = response.json()['apiKey']
Response: 200
{
"apiKey": {
"id": "b923bb15-aeb3-493a-a06f-8babc0b92fef",
"name": "Production Store",
"privateKey": "priv_4b870aa6-1f20-4889-b666-b16d33dd65e6",
"webhookUrl": "https://myshop.com/api/webhooks/tedr-v2",
"rotatedAt": null,
"createdAt": "2026-07-31T12:52:23.000Z"
}
}
| Code | Status | When |
|---|---|---|
| API_KEY_NOT_FOUND | 404 | No key with this id exists, is no longer active (revoked), or belongs to a different merchant. |
| VALIDATION_ERROR | 400 | name or webhookUrl fails validation. |
An empty request body returns the key unchanged — send only the fields you want to update.
Rotate an API key
/merchants/api-keys/:id/rotateAuth: Session JWT only — how to get a JWT.
Reissues all key material for the same key.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| id | string (UUID) | Required | The key's id, from the create, rotate, or list response. |
/merchants/api-keys/:id/rotatecurl -X POST 'https://dev-app.tedr.com/api/merchants/api-keys/b923bb15-aeb3-493a-a06f-8babc0b92fef/rotate' \
-H 'Authorization: Bearer YOUR_SESSION_JWT'
const response = await fetch(
'https://dev-app.tedr.com/api/merchants/api-keys/b923bb15-aeb3-493a-a06f-8babc0b92fef/rotate',
{
method: 'POST',
headers: { Authorization: 'Bearer YOUR_SESSION_JWT' },
},
);
const { secretKey, webhookSecret } = await response.json();
import requests
response = requests.post(
'https://dev-app.tedr.com/api/merchants/api-keys/b923bb15-aeb3-493a-a06f-8babc0b92fef/rotate',
headers={'Authorization': 'Bearer YOUR_SESSION_JWT'},
)
key = response.json()
Response: 200
{
"id": "b923bb15-aeb3-493a-a06f-8babc0b92fef",
"name": "Production Store",
"privateKey": "priv_4b870aa6-1f20-4889-b666-b16d33dd65e6",
"secretKey": "sec_kTn0aVQ7l9pS2xY4uZ8bC1dE3fG5hJ7kL9mN0pQ2rS4",
"webhookSecret": "whsec_Xy2Zb4Nc6Vd8Ef0Gh2Ij4Kl6Mn8Op0Qr2St4Uv6Wx8",
"webhookUrl": "https://myshop.com/api/webhooks/tedr-v2",
"rotatedAt": "2026-08-15T09:03:11.000Z",
"createdAt": "2026-07-31T12:52:23.000Z"
}
| Code | Status | When |
|---|---|---|
| API_KEY_NOT_FOUND | 404 | No key with this id exists, is no longer active (revoked), or belongs to a different merchant. |
privateKey, secretKey, and webhookSecret are all replaced; id, name, webhookUrl, and createdAt stay the same. The previous secretKey and webhookSecret stop working immediately. This is a POST, not a PATCH, because it is not idempotent — every call issues new material. Do not retry it after a timeout; check the key state with GET first.
Revoke an API key
/merchants/api-keys/:idAuth: Session JWT only — how to get a JWT.
Revokes the key (a soft delete — the row is not removed) and frees the active-key slot for this merchant.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| id | string (UUID) | Required | The key's id, from the create, rotate, or list response. |
/merchants/api-keys/:idcurl -X DELETE 'https://dev-app.tedr.com/api/merchants/api-keys/b923bb15-aeb3-493a-a06f-8babc0b92fef' \
-H 'Authorization: Bearer YOUR_SESSION_JWT'
await fetch('https://dev-app.tedr.com/api/merchants/api-keys/b923bb15-aeb3-493a-a06f-8babc0b92fef', {
method: 'DELETE',
headers: { Authorization: 'Bearer YOUR_SESSION_JWT' },
});
import requests
requests.delete(
'https://dev-app.tedr.com/api/merchants/api-keys/b923bb15-aeb3-493a-a06f-8babc0b92fef',
headers={'Authorization': 'Bearer YOUR_SESSION_JWT'},
)
Response: 204
| Code | Status | When |
|---|---|---|
| API_KEY_NOT_FOUND | 404 | No key with this id exists or it was already revoked. |
Returns 204 with no response body. Calling this again on the same key returns 404 API_KEY_NOT_FOUND.
