Getting started
Authentication
Get a credential, and understand which one each caller should hold.
Two kinds of caller, two kinds of credential. A service token is for your backend: it is a secret, it does not expire on its own, and it acts as the party it was issued to. A session is for a person: it comes from logging in, it expires, and it carries who they are and what they may do.
Pick by who is calling, not by which is convenient.
Signing in is on /sessions, and it needs no credential which it cannot, since it is how you get one. The password, the one-time code or the emailed code is the credential, and it travels in the request body so it never lands in an access log or a browser history.
The scope says whose it is, a merchant, a marketplace, an enterprise, and it cannot reach beyond that. description is the only way to tell two secrets apart later, because nothing derived from the secret itself is ever returned. Make it specific.
curl -X POST "https://api.tst.au.apgpay.dev/bezel/api-keys" \
-H "Authorization: Bearer $API_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"description": "Checkout service, production",
"scope": {
"merchantId": "<useMerchantIdFromSeed>",
"type": "ApiKeyScopeMerchantSecret"
},
"permissions": {
"type": "ApiKeyPermissionsStandard"
},
"expiresAt": null,
"type": "ApiKeyCmdCreate"
}'Start here rather than with a password field. One identifier can sign in more than one way , a password, an emailed link, a one-time code, or at the company's own identity provider, and this tells you which, so you show the right form instead of guessing.
Two things come back. method is what to offer: SessMethodBuiltIn lists every built-in method this person can use, and SessMethodFederated names the provider to hand off to. selectedDomainHandle is opaque, carry it into the next call unchanged. It names the permission domain your scopes resolved to, so the session starts where you asked rather than wherever the person's highest-precedence access happens to be.
It is valid for an hour and it does not travel between devices. An emailed link opened on a phone is both, so run discovery again there.
curl -X POST "https://api.tst.au.apgpay.dev/bezel/sessions" \
-H "Content-Type: application/json" \
-d '{
"identifier": {
"emailAddress": "ada@example.com",
"type": "SessIdEmail"
},
"scopes": [
{
"refId": "<useMerchantIdFromSeed>",
"type": "SessScopeMerchant"
}
],
"type": "SessCmdDiscover"
}'With SessMethodBuiltIn listing Password, this is one call. The other methods are two, SessCmdSendOtp then SessCmdSignInOtp, SessCmdSendEmailLink then SessCmdSignInEmailLink, SessCmdStartFederated then SessCmdSignInFederated, and each pair's first call tells you what it sent.
SessViewIssued.token is a bearer: present it as Authorization: Bearer and you are done. refreshToken is rotated on every use, so keep the newest one, a client holding a spent one is a single refresh away from being signed out with no way back.
curl -X POST "https://api.tst.au.apgpay.dev/bezel/sessions" \
-H "Content-Type: application/json" \
-d '{
"identifier": {
"emailAddress": "ada@example.com",
"type": "SessIdEmail"
},
"password": "correct-horse-battery-staple",
"selectedDomainHandle": "<useSelectedDomainHandleFromSeed>",
"sessionToken": null,
"type": "SessCmdSignInPassword"
}'SessViewIssued, and its token is a bearer. Use it directly; nothing from this collection is redeemed with the identity platform. Renew it with SessUpdateRefresh before it expires. SessCmdSignInCustomerOtp can instead answer SessViewContactVerified or SessViewProspect, so decode a response by its type.For a person, the session is the credential and this read is how a client discovers what it may show. capabilities is the permission matrix; rbacHash changes when the user's roles do, so a client holding a stale one knows to re-read rather than finding out through a 403.
curl -X GET "https://api.tst.au.apgpay.dev/bezel/sessions" \
-H "Authorization: Bearer $SESSION_TOKEN"See onboard a merchant.
