Skip to main content

Request and response format

Content type​

Requests with a body use JSON:

Content-Type: application/json

Responses are always application/json; charset=utf-8, including errors. There is no XML, form-encoded variant, or content negotiation.

The one exception is the OAuth2 token endpoint, which accepts application/x-www-form-urlencoded because that's required by the OAuth2 specification.

Methods​

MethodUse
GETRetrieve a resource or list. Never has side effects
POSTCreate a resource, or perform a named action
PATCHPartially update. Only the fields you send change
DELETERarely used. Most "deletions" are state changes, such as closing a card

PUT is not used. Full replacement is a poor fit for objects with server-managed fields and creates more problems than it solves.

Actions that don't fit normal CRUD use sub-paths instead of a type field in the request body:

POST /v1/cards/{card_id}/freeze
POST /v1/cards/{card_id}/unfreeze
POST /v1/cards/{card_id}/replace

Object shape​

Every object includes id, object, and created_at:

{
"id": "card_01HX8ZQ2K4M7P9RSTV",
"object": "card",
"status": "active",
"created_at": "2026-07-29T14:32:05Z",
"updated_at": "2026-07-29T15:01:44Z"
}

object tells you what kind of resource you're looking at, so you can dispatch on the object type without parsing the ID prefix. This is especially useful for polymorphic handling, such as when an object comes from a webhook payload or a mixed search result.

Field conventions​

RuleExample
snake_case field namescardholder_id, not cardholderId
Timestamps suffixed _atcreated_at, expires_at
Booleans prefixed is_ or has_is_default, has_pin
Enums lowercase snake_case"review_required", never "REVIEW_REQUIRED" or 3
Money is always an object{"amount": 1050, "currency": "SGD"}
Nullable fields documented as suchAbsent and null mean the same thing

Fields that are null are omitted from responses. You don't need to distinguish between a missing key and a null value.

Request headers​

HeaderRequiredPurpose
AuthorizationYesBearer <access token>
Content-TypeOn requests with a bodyapplication/json
Idempotency-KeyStrongly recommended on POSTSee idempotency
Aleta-VersionNoPin to a dated release

Response headers​

HeaderAlways presentPurpose
Aleta-Request-IdYesUnique per request. Include it in every support ticket
RateLimit-LimitYesSee rate limits
RateLimit-RemainingYes
RateLimit-ResetYes
Retry-AfterOn 429 and 503Seconds to wait
Deprecation / SunsetOn deprecated endpointsSee versioning

Aleta-Request-Id is probably the most useful thing to log. When you have a question about a specific request, that ID lets support find the call directly instead of having to dig through a whole thread.

Unknown fields​

Unknown fields in a request body are rejected with 400 and the error code unknown_parameter. The offending field is included in param.

This is intentional. Silently ignoring an unknown field turns a simple typo, like ammount instead of amount, into a card issued with the wrong limit without giving you any indication that something went wrong.