ID prefixes
Every object has an opaque, prefixed identifier. The prefix tells you what type of object it is; the rest of the ID has no meaning and shouldn't be parsed.
card_01HX8ZQ2K4M7P9RSTV
└──┘ └────────────────┘
type opaque
The full list
| Prefix | Object |
|---|---|
prog_ | Program |
chd_ | Cardholder |
acct_ | Account |
card_ | Card |
auth_ | Authorization |
txn_ | Transaction |
disp_ | Dispute |
tok_ | Network token |
epkg_ | eSIM data package |
esim_ | eSIM |
eord_ | eSIM order |
etop_ | eSIM top-up |
whk_ | Webhook endpoint |
evt_ | Event |
req_ | Request (used in Aleta-Request-Id and the error request_id) |
oc_ | OAuth client |
oat_ | Access token record (shown in Cockpit, never sent over the wire) |
Access tokens are different. They aren't object IDs and don't follow this
format. They're opaque secrets prefixed with at_test_ for sandbox or
at_live_ for production.
Using a token in the wrong environment will fail rather than accidentally accessing objects from the other environment.
Format
The part after the prefix is a 26-character uppercase Crockford base-32 string.
Full IDs match:
^[a-z]+_[0-9A-HJKMNP-TV-Z]{26}$
Crockford base-32 leaves out I, L, O, and U because they're easy to
confuse with other characters when copying IDs from things like support tickets.
When storing IDs, size your database column for up to 64 characters. IDs are currently shorter than that, but the format and maximum length aren't something your integration should rely on.
Rules
- Don't parse IDs. The opaque part has no meaning. It's not a timestamp, sequence number, hash, or anything else you can reproduce.
- Don't sort by ID. If you need chronological order, sort by
created_at. - Don't assume IDs are portable between environments. The same string in sandbox and production refers to unrelated objects.
- Use the prefix for basic validation. For example, if your code expects a
card_ID but receives anacct_ID, reject it before making a request. - Store IDs as strings. Keep the prefix and don't convert them to numbers.
Why IDs are opaque
There are a couple of practical reasons.
First, sequential IDs can leak information about your volume. If someone signs up and creates one card, they could potentially infer roughly how many cards you've issued. Sequential IDs also make enumeration easier.
More importantly, opaque IDs give us room to change what's happening behind the scenes. The ID you store belongs to Aleta, rather than exposing a key from some downstream system. If our internal processing changes, the IDs you've already stored can keep working.
That's intentional. Exposing an upstream identifier through the API would make that kind of change much harder.
Your own identifiers
Most resources accept a reference field for your own identifier. It's returned
when you read the resource and included in webhook payloads:
{
"id": "card_01HX8ZQ2K4M7P9RSTV",
"reference": "employee-4471-card-2"
}
reference is indexed and can be used as a filter:
curl "https://api.aletaplanet.com/v1/cards?reference=employee-4471-card-2"
reference doesn't have to be unique and isn't used for idempotency. If you
need idempotency, use Idempotency-Key.