Your first request in 60 seconds
Everything goes through one endpoint: POST /api/v1/execute. The body names the command you want and its params. That's it.
# 1. Sign up (or skip if you already have an account) curl "https://bastionary.io/api/v1/execute" \ -H "Content-Type: application/json" \ -d '{ "command": "AUTH.SIGNUP", "params": { "email": "you@example.com", "password": "CorrectHorseBatteryStaple!", "display_name": "You" } }' # 2. Log in → you get access_token + refresh_token curl "https://bastionary.io/api/v1/execute" \ -H "Content-Type: application/json" \ -d '{ "command": "AUTH.LOGIN", "params": { "email": "you@example.com", "password": "CorrectHorseBatteryStaple!" } }' # 3. Use the token on any protected command curl "https://bastionary.io/api/v1/execute" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -d '{"command":"USER.ME","params":{}}'
Every response has the same shape
Successes and failures share a single envelope. Your client code only needs to branch on ok.
{
"ok": true,
"command": "USER.ME",
"data": {
"id": "usr_01HX…",
"email": "you@example.com",
"is_superadmin": false
},
"error": null
}
{
"ok": false,
"command": "AUTH.LOGIN",
"data": null,
"error": {
"code": "AUTH_ERROR",
"message": "Invalid credentials"
}
}
Tokens, sessions, refresh
AUTH.LOGIN returns a short-lived access_token (15 min default) and a long-lived refresh_token. Put the access token in the Authorization: Bearer header. When it expires, call AUTH.REFRESH with the refresh token to get a new pair. Risk scoring, MFA, IP allowlists, and rate limits are all enforced by the Flow engine — you configure it visually, no code changes.
Authentication commands
Primitives for login, signup, MFA, password resets, and OAuth bridges.
Users
CRUD, role changes, avatar uploads, profile fields. Superadmins get the full list; users get self-service.
Sessions
Every active login is a session record. Revoke per-device or revoke-all on password change.
Teams & RBAC
Multi-tenant workspaces with per-team roles and fine-grained permissions.
Flow engine
Model the auth pipeline visually at /flows. Every trigger (login, signup, refresh…) runs the enabled flow before the hardcoded path, so you can force MFA, deny by risk, or branch on IP allowlists without redeploying.
Webhooks
HMAC-signed, at-least-once outbound deliveries with automatic retries and a dead-letter view.
Licensing
Software licenses with feature flags, seat counts, expiration, and offline activation. Replaces Keygen / Cryptlex.
Payments
5 processors wired: Stripe, Paddle, Lemon Squeezy, Paypal, Mollie. One API, pick the vendor per product.
All 341 commands
Searchable reference. Click any command to copy its fully-qualified name.
Error codes
Every failure returns one of these in error.code. They're stable — safe to branch on.
| Code | Meaning |
|---|---|
| AUTH_ERROR | Invalid credentials, expired session, or MFA required. |
| TOKEN_EXPIRED | Access token expired — call AUTH.REFRESH with your refresh token. |
| TOKEN_INVALID | Token signature failed or was rotated out. |
| PERMISSION_DENIED | Authenticated, but the user lacks the required role/permission. |
| NOT_FOUND | Referenced resource does not exist or is not visible to the caller. |
| VALIDATION_ERROR | Request body failed schema validation. See message for details. |
| RATE_LIMITED | Too many requests. Back off per the Retry-After header. |
| FLOW_BLOCKED | Your configured flow denied this request — inspect flow.reason. |
| UNKNOWN_COMMAND | Command name not in the registry. Check spelling and case. |
| INTERNAL_ERROR | Bastionary misbehaved. Traces go to /status. |
Rate limits
Defaults are generous; tune per-endpoint from the admin dashboard.
- • AUTH.LOGIN — 10 req / minute per IP (spike-guarded)
- • AUTH.FORGOT_PASSWORD — 3 req / hour per email
- • General /api/v1/execute — 600 req / minute per token
- • Webhook redelivery — 5 attempts, exponential backoff capped at 1h
SDKs
Official SDKs are thin wrappers — they speak the same command envelope. You never lose access to the underlying protocol.