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

MethodUsage
GETList or retrieve resources
POSTCreate a resource
PUTReplace a resource entirely
PATCHUpdate specific fields of a resource
DELETEDelete 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:

CodeMeaningTypical method
200 OKRequest succeededGET, PUT, PATCH
201 CreatedResource createdPOST
204 No ContentResource deletedDELETE

Error codes:

CodeMeaning
400 Bad RequestInvalid request data. See error messages for field-level details.
401 UnauthorizedNot authenticated. Provide valid credentials or token.
402 Payment RequiredThe workspace’s plan, or a workspace extension it hasn’t enabled, doesn’t cover this action. See refusals.
403 ForbiddenPermission 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 FoundResource does not exist.
405 Method Not AllowedHTTP method not supported on this endpoint.
429 Too Many RequestsRate limited. Carries a Retry-After header. See refusals.
500 Server ErrorInternal 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:

ValueMeaning
work_sessionThe request exceeds what the active work session’s scope allows
roleThe caller’s RBAC role doesn’t grant this permission
token_scopeThe API or service token doesn’t hold a scope this action requires
server_reachThe credential’s server access-control rules don’t cover this server
command_aclThe command isn’t pre-approved for this credential
approvalThe action needs a human approval that hasn’t been granted
presenceThe action needs a fresh MFA/step-up proof
riskRisk-based execution control refused the action
sudoThe sudo request was refused
planThe workspace’s plan or an extension doesn’t cover this action (402)
rateA time-based rate limit refused the request (429)

missing values:

ValueMeaning
command_preapprovalA pre-approval for this command would have let it through
approvalAn approval would have let it through
presenceFresh presence proof would have let it through
work_sessionAn active work session would have let it through
planA higher plan tier would have let it through
roleA 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.

Last updated: