API reference
Alpacon provides a RESTful API to control and manage the states of servers. The root path for the API is /api/.
API endpoint
Your workspace has a dedicated API endpoint:
https://<workspace>.<region>.alpacon.io/api/
Where:
<workspace>: Your workspace slug<region>: Your workspace region (assigned at creation)
Example: https://mycompany.us1.alpacon.io
đź’ˇ Finding your endpoint: Go to Personal Settings to view your complete workspace endpoint URL.
All API examples in this documentation use https://your-workspace.us1.alpacon.io as a placeholder. Replace it with your actual workspace endpoint.
Authentication
All API requests require an API token. Include it in the Authorization header:
Authorization: token="alpat-xxxxxxxxxxxxxxxxxx"
Create and manage tokens on the API tokens page, or via the Alpacon web console under Personal Settings > API tokens.
Global information
HTTP methods
| Method | Usage |
|---|---|
GET | List or retrieve resources |
POST | Create a resource |
PUT | Replace a resource entirely |
PATCH | Update specific fields of a resource |
DELETE | Delete a resource |
For more details, see the MDN HTTP methods reference.
URL structures
We have the following API structures.
- List or create:
/api/<collection>/<resource>/ - Retrieve, update, or delete:
/api/<collection>/<resource>/<id>/ - Extra actions:
/api/<collection>/<resource>/<id>/<action>/
For example, GET /api/iam/users/a540bf0f-8b37-4f03-8546-dd71c6b03329/ retrieves a user’s state.
Please note that all urls end with /.
Status codes
Success codes:
| Code | Meaning | Typical method |
|---|---|---|
200 OK | Request succeeded | GET, PUT, PATCH |
201 Created | Resource created | POST |
204 No Content | Resource deleted | DELETE |
Error codes:
| Code | Meaning |
|---|---|
400 Bad Request | Invalid request data. See error messages for field-level details. |
401 Unauthorized | Not authenticated. Provide valid credentials or token. |
402 Payment Required | The workspace’s plan, or a workspace extension it hasn’t enabled, doesn’t cover this action. See refusals. |
403 Forbidden | Permission denied. The authenticated user lacks required permissions. The body may be a coded refusal, or DRF’s plain {"detail": "..."} on a check not yet migrated to the coded shape. |
404 Not Found | Resource does not exist. |
405 Method Not Allowed | HTTP method not supported on this endpoint. |
429 Too Many Requests | Rate limited. Carries a Retry-After header. See refusals. |
500 Server Error | Internal server error. |
For the full list of HTTP status codes, see the MDN HTTP status reference.
Error messages
Every 400 Bad Request carries a machine-readable code, and additionally carries field_errors when Alpacon can pin the problem to a field—or to the request as a whole, under the non_field_errors key inside field_errors. Other error statuses carry code only where the check that produced them has been migrated to this coded shape; see the status codes table above and each endpoint’s own error table for which ones have.
For example, when you are changing a user’s password, if the new password does not pass server-side validation, you will see a response like this. The field name, new_password, is used as the key inside field_errors. Build your integration against code, not message—message is often a generic string like “Invalid input.” and isn’t guaranteed to stay in English.
HTTP 400 Bad Request
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"code": "password_too_short",
"field_errors": {
"new_password": [
{
"code": "password_too_short",
"message": "Invalid input."
}
]
}
}
field_errors maps each field to a list of {code, message} entries—a field can carry more than one entry when more than one check on it fails.
An error that isn’t about one specific field—for example, an object-level rule that depends on more than one value in the request—is filed under the non_field_errors key inside field_errors instead of a field name:
{
"code": "approval_superuser_approve_required",
"field_errors": {
"non_field_errors": [
{
"code": "approval_superuser_approve_required",
"message": "Invalid input."
}
]
}
}
Not every 400 carries field_errors. A validation failure raised without a specific field—for example, deleting the only remaining owner of a group—carries only the top-level code:
{
"code": "user_unique_group_owner"
}
Refusals
Available from Alpacon server 2.37.0.
A 402, 403, 405, or 429 response that already carries a code may additionally carry two fields that explain why the request was refused:
gate: the class of control that refused the request.missing: the class of prerequisite that would have let the request through—or, for a token-scope refusal only, the literal scope string the credential lacks.
Both fields are optional and appear only where the server recorded them at the moment of refusal; treat their absence like any other field you don’t recognize and ignore it. Neither field is ever present on a 401—an unauthenticated caller may still see a body (DRF’s own {"detail": "..."}), just never gate or missing. And not every 403 is coded yet: some permission checks on the platform still return that same uncoded {"detail": "..."} shape, with no code, gate, or missing at all. Check for code before reading gate or missing.
gate values:
| Value | Meaning |
|---|---|
work_session | The request exceeds what the active work session’s scope allows |
role | The caller’s RBAC role doesn’t grant this permission |
token_scope | The API or service token doesn’t hold a scope this action requires |
server_reach | The credential’s server access-control rules don’t cover this server |
command_acl | The command isn’t pre-approved for this credential |
approval | The action needs a human approval that hasn’t been granted |
presence | The action needs a fresh MFA/step-up proof |
risk | Risk-based execution control refused the action |
sudo | The sudo request was refused |
plan | The workspace’s plan or an extension doesn’t cover this action (402) |
rate | A time-based rate limit refused the request (429) |
missing values:
| Value | Meaning |
|---|---|
command_preapproval | A pre-approval for this command would have let it through |
approval | An approval would have let it through |
presence | Fresh presence proof would have let it through |
work_session | An active work session would have let it through |
plan | A higher plan tier would have let it through |
role | A role granting this permission would have let it through |
scope:<resource>:<action> | The one literal form missing may take: scope: followed by the scope the token lacks, in the platform’s own resource:action form—e.g. scope:server:update names the server:update scope. The scope: prefix belongs to this refusal envelope; the scope itself, wherever else you see or request it (on the token, in the scope catalog), is just server:update. A single string, or a list of strings when more than one scope is missing. |
Example—role refusal:
{
"code": "rbac_permission_required",
"gate": "role",
"missing": "role"
}
Example—token scope missing:
{
"code": "api_token_scope_missing",
"gate": "token_scope",
"missing": "scope:server:update"
}
gate and missing never carry a rule’s contents, another principal’s identity, or a path to where the missing thing could be requested—only the class of control and the class of prerequisite. Treat any field on this envelope you don’t recognize as informational and safe to ignore.
Alpacon features
Following documents describe the details about each collection of API.