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
| Status | Meaning | Common 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 Message | Cause | Fix |
|---|---|---|
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"))
}
}
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.