Enterprise API keys
A workspace API key belongs to one workspace and starts with rpe_. Admins and owners manage keys in the dashboard (Developers) or with these endpoints from a dashboard session. A key can never list, create or revoke keys itself, so a leaked key cannot make itself permanent. The rules for using a key are in Enterprise overview.
The three routes share a budget of 60 calls a minute per user. A workspace can have 10 active keys.
Endpoints
| Method | Path | Endpoint | Auth |
|---|---|---|---|
GET | /v1/enterprise/api-keys | List API keys | admin, dashboard only |
POST | /v1/enterprise/api-keys | Create an API key | admin, dashboard only |
DELETE | /v1/enterprise/api-keys/:id | Revoke an API key | admin, dashboard only |
List API keys
Active keys, newest first. Only a short preview of each key is shown.
- Auth: Dashboard session only. API keys, workspace or personal, get
403 KEY_NOT_ALLOWED. - Role:
adminorowner. - Rate limit: 60 a minute per user for this route, plus the shared 300 a minute.
- Idempotency: Not needed. Reading has no side effects.
Example request
curl https://api.rekberpay.com/v1/enterprise/api-keys \
-b cookies.txt \
-H "X-Enterprise-Account: $WORKSPACE_ID"const res = await fetch('https://api.rekberpay.com/v1/enterprise/api-keys', {
headers: {
Cookie: process.env.REKBERPAY_SESSION_COOKIE, // '__Host-rekber_session=...' from POST /v1/auth/login
'X-Enterprise-Account': process.env.WORKSPACE_ID,
},
});
const data = await res.json();import os
import requests
session = requests.Session()
# The session cookie from POST /v1/auth/login
session.cookies.set("__Host-rekber_session", os.environ["REKBERPAY_SESSION"], domain="api.rekberpay.com")
resp = session.get(
"https://api.rekberpay.com/v1/enterprise/api-keys",
headers={
"X-Enterprise-Account": os.environ["WORKSPACE_ID"],
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/api-keys');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEJAR => 'cookies.txt',
CURLOPT_COOKIEFILE => 'cookies.txt',
CURLOPT_HTTPHEADER => [
'X-Enterprise-Account: ' . getenv('WORKSPACE_ID'),
],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK
{
"success": true,
"data": {
"items": [
{
"id": "44444443-4444-4444-8444-444444444444",
"label": "BI read-only",
"scopes": [
"read"
],
"key_preview": "rpe_q8Zt…",
"created_by": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"last_used_at": null,
"created_at": "2026-10-13T14:33:15.359Z",
"status": "active"
},
{
"id": "22222223-2222-4222-8222-222222222222",
"label": "ERP sync",
"scopes": [
"read",
"write"
],
"key_preview": "rpe_q8Zt…",
"created_by": {
"id": "33333333-3333-4333-8333-333333333333",
"name": "Dimas Hidayat"
},
"last_used_at": null,
"created_at": "2026-10-13T14:33:15.354Z",
"status": "inactive"
}
],
"limit": 10
}
}status is active, or inactive when the key is not revoked but no longer works because the member who created it left the workspace (or was invited again later). Removing a member revokes their keys; revoke an inactive key to free its slot.
Errors
| Status | Code | Cause |
|---|---|---|
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
403 | FORBIDDEN | Your role is below admin |
403 | KEY_NOT_ALLOWED | Called with an API key instead of a dashboard session |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |
A real KEY_NOT_ALLOWED answer:
403 Forbidden
{
"success": false,
"error": {
"code": "KEY_NOT_ALLOWED",
"message": "Workspace API keys cannot call this route. Sign in to the dashboard instead."
}
}Create an API key
Creates a key and returns the plaintext in key once. Only its SHA-256 hash is stored, so a lost key cannot be recovered: revoke it and create a new one. A read key can call every GET and acts as a viewer. A read and write key acts with the role of the admin who created it, and stops working if that person leaves the workspace.
- Auth: Dashboard session only. API keys, workspace or personal, get
403 KEY_NOT_ALLOWED. - Role:
adminorowner. - Rate limit: 60 a minute per user for this route, plus the shared 300 a minute.
- Idempotency: Not supported on purpose: the plaintext key is never stored in the replay cache. A double submit at most creates a second key that you can revoke.
Body (JSON)
| Name | Type | Required | Description |
|---|---|---|---|
label | string | Yes | 2 to 60 characters |
scopes | string[] | Yes | ["read"] or ["read", "write"] |
Example request
curl -X POST https://api.rekberpay.com/v1/enterprise/api-keys \
-b cookies.txt \
-H "X-CSRF-Token: $CSRF_TOKEN" \
-H "X-Enterprise-Account: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"label": "ERP sync",
"scopes": [
"read",
"write"
]
}'const res = await fetch('https://api.rekberpay.com/v1/enterprise/api-keys', {
method: 'POST',
headers: {
Cookie: process.env.REKBERPAY_SESSION_COOKIE, // '__Host-rekber_session=...' from POST /v1/auth/login
'X-CSRF-Token': process.env.REKBERPAY_CSRF_TOKEN,
'X-Enterprise-Account': process.env.WORKSPACE_ID,
'Content-Type': 'application/json',
},
body: JSON.stringify({
label: "ERP sync",
scopes: [
"read",
"write"
]
}),
});
const data = await res.json();import os
import requests
session = requests.Session()
# The session cookie from POST /v1/auth/login
session.cookies.set("__Host-rekber_session", os.environ["REKBERPAY_SESSION"], domain="api.rekberpay.com")
resp = session.post(
"https://api.rekberpay.com/v1/enterprise/api-keys",
headers={
"X-CSRF-Token": os.environ["REKBERPAY_CSRF_TOKEN"],
"X-Enterprise-Account": os.environ["WORKSPACE_ID"],
},
json={
"label": "ERP sync",
"scopes": [
"read",
"write",
],
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/api-keys');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEJAR => 'cookies.txt',
CURLOPT_COOKIEFILE => 'cookies.txt',
CURLOPT_HTTPHEADER => [
'X-CSRF-Token: ' . getenv('REKBERPAY_CSRF_TOKEN'),
'X-Enterprise-Account: ' . getenv('WORKSPACE_ID'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'label' => 'ERP sync',
'scopes' => [
'read',
'write',
],
]),
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
201 Created
{
"success": true,
"data": {
"id": "22222223-2222-4222-8222-222222222222",
"label": "ERP sync",
"scopes": [
"read",
"write"
],
"key_preview": "rpe_q8Zt…",
"created_by": {
"id": "33333333-3333-4333-8333-333333333333",
"name": null
},
"last_used_at": null,
"created_at": "2026-10-13T14:33:15.355Z",
"status": "active",
"key": "rpe_q8Zt3VnK1mW0xPe7Rk2LsY5bHd9JcAf4GuN6oTiQ1vE"
}
}Errors
| Status | Code | Cause |
|---|---|---|
400 | INVALID_BODY | Label or scopes missing or not valid |
409 | KEY_LIMIT | The workspace already has 10 active keys |
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
403 | FORBIDDEN | Your role is below admin |
403 | KEY_NOT_ALLOWED | Called with an API key instead of a dashboard session |
403 | CSRF_FAILED | Session call without the matching X-CSRF-Token header |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |
A real INVALID_BODY answer:
400 Bad Request
{
"success": false,
"error": {
"code": "INVALID_BODY",
"message": "Invalid request.",
"details": {
"formErrors": [],
"fieldErrors": {
"label": [
"Too small: expected string to have >=2 characters"
],
"scopes": [
"Too small: expected array to have >=1 items"
]
}
}
}
}Revoke an API key
The key stops working at once. api_key.revoked is recorded in the event feed.
- Auth: Dashboard session only. API keys, workspace or personal, get
403 KEY_NOT_ALLOWED. - Role:
adminorowner. - Rate limit: 60 a minute per user for this route, plus the shared 300 a minute.
- Idempotency: Send an
Idempotency-Keyheader (UUID v4). A retry with the same key and body returns the first answer withIdempotent-Replay: true.
Path parameters
| Name | Type | Description |
|---|---|---|
id | UUID | The key ID from the list |
Example request
curl -X DELETE https://api.rekberpay.com/v1/enterprise/api-keys/55555553-5555-4555-8555-555555555555 \
-b cookies.txt \
-H "X-CSRF-Token: $CSRF_TOKEN" \
-H "X-Enterprise-Account: $WORKSPACE_ID" \
-H "Idempotency-Key: 66666663-6666-4666-8666-666666666666"const res = await fetch('https://api.rekberpay.com/v1/enterprise/api-keys/55555553-5555-4555-8555-555555555555', {
method: 'DELETE',
headers: {
Cookie: process.env.REKBERPAY_SESSION_COOKIE, // '__Host-rekber_session=...' from POST /v1/auth/login
'X-CSRF-Token': process.env.REKBERPAY_CSRF_TOKEN,
'X-Enterprise-Account': process.env.WORKSPACE_ID,
'Idempotency-Key': '66666663-6666-4666-8666-666666666666',
},
});
const data = await res.json();import os
import requests
session = requests.Session()
# The session cookie from POST /v1/auth/login
session.cookies.set("__Host-rekber_session", os.environ["REKBERPAY_SESSION"], domain="api.rekberpay.com")
resp = session.delete(
"https://api.rekberpay.com/v1/enterprise/api-keys/55555553-5555-4555-8555-555555555555",
headers={
"X-CSRF-Token": os.environ["REKBERPAY_CSRF_TOKEN"],
"X-Enterprise-Account": os.environ["WORKSPACE_ID"],
"Idempotency-Key": "66666663-6666-4666-8666-666666666666",
},
timeout=30,
)
print(resp.status_code, resp.json())<?php
$ch = curl_init('https://api.rekberpay.com/v1/enterprise/api-keys/55555553-5555-4555-8555-555555555555');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEJAR => 'cookies.txt',
CURLOPT_COOKIEFILE => 'cookies.txt',
CURLOPT_HTTPHEADER => [
'X-CSRF-Token: ' . getenv('REKBERPAY_CSRF_TOKEN'),
'X-Enterprise-Account: ' . getenv('WORKSPACE_ID'),
'Idempotency-Key: 66666663-6666-4666-8666-666666666666',
],
]);
$response = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);Example response
200 OK
{
"success": true,
"data": {
"id": "55555553-5555-4555-8555-555555555555",
"revoked": true
}
}Errors
| Status | Code | Cause |
|---|---|---|
404 | NOT_FOUND | No active key with this ID |
401 | UNAUTHORIZED, SESSION_EXPIRED, INVALID_TOKEN | No session or key, the session expired, or the key is wrong or revoked |
400 | INVALID_ACCOUNT | X-Enterprise-Account is not a UUID |
403 | NO_WORKSPACE | You are not an active member of that workspace, or the key belongs to another one |
403 | FORBIDDEN | Your role is below admin |
403 | KEY_NOT_ALLOWED | Called with an API key instead of a dashboard session |
403 | CSRF_FAILED | Session call without the matching X-CSRF-Token header |
429 | RATE_LIMITED | Over the rate limit. Wait for Retry-After seconds |