Error Codes

Engine and account routes answer an error with an application/json body holding a single error field: a human-readable message. There is no machine-readable error code, so branch on the HTTP status, not on the text. Two answers are not JSON: the firewall's 429 is text/plain "Too Many Requests", and an IP it has banned gets 403 with an empty body.

Error Response Format

{
  "error": "process \"order-processing\" not deployed"
}

One route adds two fields. When POST /api/v1/process-instances cannot take its credit, the 402 body is:

{
  "error": "insufficient credits: have 0, need 1",
  "balance": 0,
  "buy": "https://priostack.com/credits"
}

balance is currently always 0, whatever the real balance (read GET /api/credits for it), and packs are bought at priostack.com/wallet. The same error field can also read account is disabled, or unknown api key for a key with no account. Every other charged route answers {"error":"insufficient credits"}.

HTTP Status Codes

StatusMeaningCommon Cause
400 Bad Request The body is not valid JSON (invalid json), or a deploy was sent with a Content-Type other than XML or multipart. An invalid model is a 422, not a 400.
401 Unauthorized No API key was sent, or the key does not belong to an account on routes that check it. Send it as X-API-Key, Authorization: Bearer or ?api_key=; see Authentication.
402 Payment Required The balance cannot cover a charged operation: an instance start, a case start, a decision evaluation, a deploy through /api/v1/models, a task completion through /api/v1/tasks/{id}/complete, an agent query or a device enrolment. Reads, job activation and job completion never answer 402. Buy a pack at priostack.com/wallet, or wait for the daily grant of 100 credits.
403 Forbidden With an empty body: the firewall has banned your IP after three rate-limit strikes (2 hours, doubling on each repeat up to 7 days). A few account routes also answer 403 with a JSON error, for example buying credits on a disabled account.
404 Not Found The instance, definition, job, incident or webhook does not exist or belongs to another account (the two cases answer the same). Completing a job that is already completed also answers 404.
409 Conflict Not returned by the workflow routes. Redeploying identical BPMN answers 200 with the existing definition and version, and completing a job a second time answers 404 job not found.
422 Unprocessable Entity The request is well formed but the engine refuses it: starting a process with an unknown bpmnProcessId, deploying a model that does not parse or compile, evaluating a decision that is not deployed, or cancelling an instance that is not active.
429 Too Many Requests More than 300 requests in 60 seconds from your IP address, across all paths. The body is plain text. See Rate Limits.
500 Internal Server Error Unexpected server-side error. If this persists, contact support with the X-Request-ID from the response headers.
503 Service Unavailable POST /api/signup answers 503 once 10,000 free accounts exist. Otherwise the service is briefly unavailable, for example during a restart: retry with backoff.

Common Error Messages

Error MessageCauseFix
missing api key, api key required, unauthorized 401: no key in the request. The wording depends on the route. Send the key in X-API-Key. Webhook routes accept only that header.
insufficient credits: have 0, need 1 402 on instance start: the balance is too low. Other charged routes say insufficient credits. Buy a pack at priostack.com/wallet. Every verified account also gains 100 credits for each full day since its last grant, paid at the next check (every 6 hours).
process "order-processing" not deployed 422: no process with that bpmnProcessId is deployed under your API key. Deploy the BPMN first via POST /api/v1/process-definitions, with the same key.
deploy failed: <parser or compiler error> 422: the XML does not parse or the model does not compile. The message says where. Fix the model. There is no limit on the number of elements.
job not found 404: the job key is unknown, already completed, failed to zero retries, belongs to a cancelled instance or to another account. After a lost response, do not retry blindly: read GET /api/v1/process-instances/{processInstanceKey} to see whether the completion was applied.
invalid json 400: the body is not JSON. POST /api/v1/jobs/activate reads the body before the key, so an empty body answers this too. Send a JSON body, at least {}.
instance run_1ce49bbc5260b39423bd is not active (state: INCIDENT) 422 on DELETE /api/v1/process-instances/{key}: only an ACTIVE instance can be cancelled. Find failed runs with GET /api/v1/process-instances?filter.state=INCIDENT. An instance in INCIDENT cannot be resumed or cancelled over the API today.
decision "discount" not deployed for this tenant 422 on POST /api/v1/decisions/{id}/evaluate. A Business Rule Task whose decision is not deployed puts its instance in INCIDENT instead. Deploy the DMN with POST /api/v1/models before evaluating it or starting the process that uses it.
Too Many Requests (plain text) 429: more than 300 requests in the last 60 seconds from your IP address. Back off for about a minute; rejected requests count too. Sleep between empty job polls.
free tier is currently full 503 on POST /api/signup: 10,000 free accounts exist. Try again later or contact support@priostack.com.

Error Handling Example

package priostack

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

// checkResponse turns a Priostack response into an error. Success is any 2xx:
// job activation answers 200, instance start 201, job complete and fail 204.
func checkResponse(resp *http.Response) error {
    if resp.StatusCode/100 == 2 {
        return nil
    }
    body, _ := io.ReadAll(io.LimitReader(resp.Body, 64<<10))
    msg := string(body) // 429 is plain text and a banned IP's 403 is empty
    var e struct {
        Error string `json:"error"`
    }
    if json.Unmarshal(body, &e) == nil && e.Error != "" {
        msg = e.Error
    }
    switch resp.StatusCode {
    case http.StatusUnauthorized:
        return fmt.Errorf("invalid or missing API key: %s", msg)
    case http.StatusPaymentRequired:
        return fmt.Errorf("out of credits (%s): buy a pack at https://priostack.com/wallet or wait for the daily grant", msg)
    case http.StatusTooManyRequests:
        return fmt.Errorf("rate limited: wait about 60 seconds before retrying")
    case http.StatusForbidden:
        return fmt.Errorf("forbidden %q (an empty body means the firewall banned this IP)", msg)
    default:
        return fmt.Errorf("API error %d: %s (request id %s)", resp.StatusCode, msg, resp.Header.Get("X-Request-ID"))
    }
}
Include X-Request-ID in bug reports: Every response includes an X-Request-ID header with a unique identifier. When contacting support about a 5xx error, include this ID so the team can locate the relevant server logs.