Create an API Key
Persists a new API key from a caller-supplied public key. The caller generates (or already holds) the keypair — UDX never receives the private key. The public key must be RSA with a modulus between 2048 and 4096 bits, inclusive.
POST
/api_keysAuthorization
UDX-Access-KeyAPI key · headerrequiredOpaque API key ID.
UDX-Access-TimestampAPI key · headerrequiredUnix time in milliseconds, within 30 seconds of server time.
UDX-Access-SignatureAPI key · headerrequiredStandard-base64 RSA-PSS/SHA-256 signature with a 32-byte salt. Sign `timestamp + METHOD + path`; for requests with a body, append `hex(SHA256(body))`; append `UDX-Idempotency-Key` when it is present.
or
AuthorizationBearer token (JWT) · headerrequiredBearer token for an active signed-in UDX user.
Header parameters
UDX-VersionstringDated API version (YYYY-MM-DD). Omit to use the oldest supported version. An unknown version returns `400`.
max length 10
UDX-Idempotency-KeystringOptional client-generated key that makes a mutation safe to retry. Reuse it only for a byte-identical retry. When present, it is included in the request signature; omitting it disables UDX idempotency for that request.
min length 1 · max length 255
Request body
requiredapplication/jsonnamestringrequiredA nickname for the new key, so you can tell your keys apart later.
min length 1 · max length 200
public_keystringrequiredPEM-encoded RSA public key. Rejected with 400 if it isn't a single well-formed PEM block containing a PKIX-encoded RSA public key with a modulus between 2048 and 4096 bits, inclusive.
min length 1 · max length 10000
scopesAPIKeyScope[]requiredThe permissions to grant this key. Fixed for its lifetime — granting a different set of scopes later means creating a new key and revoking this one.
min items 1 · max items 5
account_idstring | nullRestricts the key to a single account by its opaque public identifier. Omit (or null) to leave the key unrestricted, usable against every account accessible to the caller. Rejected with 403 if the caller cannot access the given account.
Responses
201The created API key's metadata.
api_keyAPIKeyrequiredAn API key's metadata. Never includes the private key because UDX never receives it.
Show propertiesHide properties
idobjectrequiredThe API key's public identifier.
Show propertiesHide properties
objectnamestringrequiredThe nickname given to this key at creation.
max length 200
scopesAPIKeyScope[]requiredThe permissions this key was granted at creation. Fixed for its lifetime — a different set of scopes means a new key.
max items 5
account_idstring | nullThe opaque public identifier of the single account this key is restricted to, or null if it's usable against every account accessible to the caller.
created_atstring<date-time>requiredUDX timestamps use RFC 3339; requests require an explicit UTC offset and zero to six fractional digits; responses use UTC with exactly six fractional digits. When this key was created. Keys are immutable, so nothing about it changes after this point except being revoked.
max length 40
400Request doesn't conform to this spec.
typestring<uri-reference>A stable URI identifying the error's general category. Usually absent (equivalent to "about:blank" per the RFC); documented rate controls use closed URN values so callers can distinguish same-status retry causes without parsing localized prose.
max length 2048
titlestringrequiredA localized, human-readable summary of the error category. Stable for a given status code and resolved language — clients must not use it as a machine-readable discriminator.
max length 40
statusintegerrequiredThe HTTP status code, repeated from the response itself.
detailstringA localized, human-readable explanation specific to this occurrence. Clients must not parse it for machine-readable information.
max length 500
instancestring<uri-reference>An identifier for this specific occurrence, so it can be correlated with server-side logs if you need to report it.
max length 100
401No valid credential was provided.
typestring<uri-reference>A stable URI identifying the error's general category. Usually absent (equivalent to "about:blank" per the RFC); documented rate controls use closed URN values so callers can distinguish same-status retry causes without parsing localized prose.
max length 2048
titlestringrequiredA localized, human-readable summary of the error category. Stable for a given status code and resolved language — clients must not use it as a machine-readable discriminator.
max length 40
statusintegerrequiredThe HTTP status code, repeated from the response itself.
detailstringA localized, human-readable explanation specific to this occurrence. Clients must not parse it for machine-readable information.
max length 500
instancestring<uri-reference>An identifier for this specific occurrence, so it can be correlated with server-side logs if you need to report it.
max length 100
403The caller cannot access `account_id`, or the credential lacks the `keys:manage` scope.
typestring<uri-reference>A stable URI identifying the error's general category. Usually absent (equivalent to "about:blank" per the RFC); documented rate controls use closed URN values so callers can distinguish same-status retry causes without parsing localized prose.
max length 2048
titlestringrequiredA localized, human-readable summary of the error category. Stable for a given status code and resolved language — clients must not use it as a machine-readable discriminator.
max length 40
statusintegerrequiredThe HTTP status code, repeated from the response itself.
detailstringA localized, human-readable explanation specific to this occurrence. Clients must not parse it for machine-readable information.
max length 500
instancestring<uri-reference>An identifier for this specific occurrence, so it can be correlated with server-side logs if you need to report it.
max length 100
409This key is still in use or its response cannot be replayed. The mutation may have run; check `detail` before retrying.
typestring<uri-reference>A stable URI identifying the error's general category. Usually absent (equivalent to "about:blank" per the RFC); documented rate controls use closed URN values so callers can distinguish same-status retry causes without parsing localized prose.
max length 2048
titlestringrequiredA localized, human-readable summary of the error category. Stable for a given status code and resolved language — clients must not use it as a machine-readable discriminator.
max length 40
statusintegerrequiredThe HTTP status code, repeated from the response itself.
detailstringA localized, human-readable explanation specific to this occurrence. Clients must not parse it for machine-readable information.
max length 500
instancestring<uri-reference>An identifier for this specific occurrence, so it can be correlated with server-side logs if you need to report it.
max length 100
422This key belongs to a different request.
typestring<uri-reference>A stable URI identifying the error's general category. Usually absent (equivalent to "about:blank" per the RFC); documented rate controls use closed URN values so callers can distinguish same-status retry causes without parsing localized prose.
max length 2048
titlestringrequiredA localized, human-readable summary of the error category. Stable for a given status code and resolved language — clients must not use it as a machine-readable discriminator.
max length 40
statusintegerrequiredThe HTTP status code, repeated from the response itself.
detailstringA localized, human-readable explanation specific to this occurrence. Clients must not parse it for machine-readable information.
max length 500
instancestring<uri-reference>An identifier for this specific occurrence, so it can be correlated with server-side logs if you need to report it.
max length 100
429The Member's request rate is exhausted. Wait for `Retry-After`.
typestring<uri-reference>A stable URI identifying the error's general category. Usually absent (equivalent to "about:blank" per the RFC); documented rate controls use closed URN values so callers can distinguish same-status retry causes without parsing localized prose.
max length 2048
titlestringrequiredA localized, human-readable summary of the error category. Stable for a given status code and resolved language — clients must not use it as a machine-readable discriminator.
max length 40
statusintegerrequiredThe HTTP status code, repeated from the response itself.
detailstringA localized, human-readable explanation specific to this occurrence. Clients must not parse it for machine-readable information.
max length 500
instancestring<uri-reference>An identifier for this specific occurrence, so it can be correlated with server-side logs if you need to report it.
max length 100
500Something went wrong on UDX's side.
typestring<uri-reference>A stable URI identifying the error's general category. Usually absent (equivalent to "about:blank" per the RFC); documented rate controls use closed URN values so callers can distinguish same-status retry causes without parsing localized prose.
max length 2048
titlestringrequiredA localized, human-readable summary of the error category. Stable for a given status code and resolved language — clients must not use it as a machine-readable discriminator.
max length 40
statusintegerrequiredThe HTTP status code, repeated from the response itself.
detailstringA localized, human-readable explanation specific to this occurrence. Clients must not parse it for machine-readable information.
max length 500
instancestring<uri-reference>An identifier for this specific occurrence, so it can be correlated with server-side logs if you need to report it.
max length 100
503Request admission is unavailable. Retry later.
typestring<uri-reference>A stable URI identifying the error's general category. Usually absent (equivalent to "about:blank" per the RFC); documented rate controls use closed URN values so callers can distinguish same-status retry causes without parsing localized prose.
max length 2048
titlestringrequiredA localized, human-readable summary of the error category. Stable for a given status code and resolved language — clients must not use it as a machine-readable discriminator.
max length 40
statusintegerrequiredThe HTTP status code, repeated from the response itself.
detailstringA localized, human-readable explanation specific to this occurrence. Clients must not parse it for machine-readable information.
max length 500
instancestring<uri-reference>An identifier for this specific occurrence, so it can be correlated with server-side logs if you need to report it.
max length 100
Request
curl -X POST "https://{hostname}/api_keys" \
-H "UDX-Access-Key: API_KEY_ID" \
-H "UDX-Access-Timestamp: UNIX_MILLISECONDS" \
-H "UDX-Access-Signature: BASE64_SIGNATURE" \
-H "Content-Type: application/json" \
-d '{
"name": "trading bot",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs73XRQ6kA8x4YhlozC6B\nF0+5iLfTU/MuYmvTCSaLH8/pgJBS+mggm5QeC0OkazwNMc1fzyx2n7OUFeN9Qm/M\nfyELPozTSSvPGebwUhsKyOZGKtO92HsejU075xnVsYWiYU+OA6DDxc5tR4mhMxS3\nDnHkwCQ1P2Z6SkaiYVQ2mMrGewT7TGlUYaZo+FI0svpJzMsJ5tNqjdh/9dsUC7WM\nATwMcoERX2Zb66ylk8dCw8rOoIE07/MTQfoH/z/++7rz+Jqt+KmnfKvSOOIr/WnE\nRLuBnd7OyKq7uCLGcWGq79FEa1ToWNcHAsLReElUU0shkqjRjaD1iKm83zh4XLWf\ngQIDAQAB\n-----END PUBLIC KEY-----",
"scopes": [
"read",
"trade"
]
}'const response = await fetch("https://{hostname}/api_keys", {
method: "POST",
headers: {
"UDX-Access-Key": "API_KEY_ID",
"UDX-Access-Timestamp": "UNIX_MILLISECONDS",
"UDX-Access-Signature": "BASE64_SIGNATURE",
"Content-Type": "application/json"
},
body: JSON.stringify({
"name": "trading bot",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs73XRQ6kA8x4YhlozC6B\nF0+5iLfTU/MuYmvTCSaLH8/pgJBS+mggm5QeC0OkazwNMc1fzyx2n7OUFeN9Qm/M\nfyELPozTSSvPGebwUhsKyOZGKtO92HsejU075xnVsYWiYU+OA6DDxc5tR4mhMxS3\nDnHkwCQ1P2Z6SkaiYVQ2mMrGewT7TGlUYaZo+FI0svpJzMsJ5tNqjdh/9dsUC7WM\nATwMcoERX2Zb66ylk8dCw8rOoIE07/MTQfoH/z/++7rz+Jqt+KmnfKvSOOIr/WnE\nRLuBnd7OyKq7uCLGcWGq79FEa1ToWNcHAsLReElUU0shkqjRjaD1iKm83zh4XLWf\ngQIDAQAB\n-----END PUBLIC KEY-----",
"scopes": [
"read",
"trade"
]
})
});import requests
response = requests.post(
"https://{hostname}/api_keys",
headers={
"UDX-Access-Key": "API_KEY_ID",
"UDX-Access-Timestamp": "UNIX_MILLISECONDS",
"UDX-Access-Signature": "BASE64_SIGNATURE",
"Content-Type": "application/json"
},
json={
"name": "trading bot",
"public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs73XRQ6kA8x4YhlozC6B\nF0+5iLfTU/MuYmvTCSaLH8/pgJBS+mggm5QeC0OkazwNMc1fzyx2n7OUFeN9Qm/M\nfyELPozTSSvPGebwUhsKyOZGKtO92HsejU075xnVsYWiYU+OA6DDxc5tR4mhMxS3\nDnHkwCQ1P2Z6SkaiYVQ2mMrGewT7TGlUYaZo+FI0svpJzMsJ5tNqjdh/9dsUC7WM\nATwMcoERX2Zb66ylk8dCw8rOoIE07/MTQfoH/z/++7rz+Jqt+KmnfKvSOOIr/WnE\nRLuBnd7OyKq7uCLGcWGq79FEa1ToWNcHAsLReElUU0shkqjRjaD1iKm83zh4XLWf\ngQIDAQAB\n-----END PUBLIC KEY-----",
"scopes": [
"read",
"trade"
]
},
)Response
{
"api_key": {
"id": "3517456229634185646",
"name": "trading bot",
"scopes": [
"read",
"trade"
],
"account_id": null,
"created_at": "2026-06-01T12:00:00.000000Z"
}
}{
"api_key": {
"id": "7425944046791590247",
"name": "readonly dashboard",
"scopes": [
"read"
],
"account_id": "6820943174116859281",
"created_at": "2026-06-15T09:30:00.000000Z"
}
}{
"title": "Bad Request",
"status": 400,
"detail": "public_key must be a single PEM-encoded RSA public key with a modulus between 2048 and 4096 bits",
"instance": "urn:request-id:8f14e45f-ceea-4abc-a743-e89a34d3f6ef"
}{
"title": "Bad Request",
"status": 400,
"detail": "invalid request parameters",
"instance": "urn:request-id:8f14e45f-ceea-4abc-a743-e89a34d3f6ef"
}{
"title": "Bad Request",
"status": 400,
"detail": "invalid request parameters",
"instance": "urn:request-id:8f14e45f-ceea-4abc-a743-e89a34d3f6ef"
}{
"title": "Bad Request",
"status": 400,
"detail": "unknown UDX-Version \"1999-01-01\"; supported versions: 2026-07-15",
"instance": "urn:request-id:8f14e45f-ceea-4abc-a743-e89a34d3f6ef"
}{
"title": "Unauthorized",
"status": 401,
"detail": "invalid authentication credentials",
"instance": "urn:request-id:8f14e45f-ceea-4abc-a743-e89a34d3f6ef"
}{
"title": "Forbidden",
"status": 403,
"detail": "account access denied",
"instance": "urn:request-id:8f14e45f-ceea-4abc-a743-e89a34d3f6ef"
}{
"title": "Forbidden",
"status": 403,
"detail": "insufficient permissions",
"instance": "urn:request-id:8f14e45f-ceea-4abc-a743-e89a34d3f6ef"
}{
"title": "Conflict",
"status": 409,
"detail": "a request with this UDX-Idempotency-Key is already in progress",
"instance": "urn:request-id:8f14e45f-ceea-4abc-a743-e89a34d3f6ef"
}{
"title": "Unprocessable Entity",
"status": 422,
"detail": "UDX-Idempotency-Key was already used for a different request",
"instance": "urn:request-id:8f14e45f-ceea-4abc-a743-e89a34d3f6ef"
}{
"type": "urn:udx:problem:member-rate-limit-exceeded",
"title": "Too Many Requests",
"status": 429,
"detail": "this member has temporarily exceeded its request rate",
"instance": "urn:request-id:8f14e45f-ceea-4abc-a743-e89a34d3f6ef"
}{
"title": "Internal Server Error",
"status": 500,
"detail": "internal error",
"instance": "urn:request-id:8f14e45f-ceea-4abc-a743-e89a34d3f6ef"
}{
"type": "urn:udx:problem:request-admission-unavailable",
"title": "Service Unavailable",
"status": 503,
"detail": "request admission is temporarily unavailable",
"instance": "urn:request-id:8f14e45f-ceea-4abc-a743-e89a34d3f6ef"
}