Errors & Response Format¶
Standard error shape¶
Errors across the API return the same shape. Fields are omitted when null, so a typical error response is short:
at the HTTP status the error corresponds to. The full field set, when present:
| Field | Type | Meaning |
|---|---|---|
httpStatusCode |
integer | Mirrors the response's actual HTTP status |
success |
boolean | false on any error path |
message |
string | Optional human-readable summary, separate from errorDescription |
errorCode |
integer | Numeric error code — see Reference → Error Codes |
errorDescription |
string | The resolved error message |
responseBody |
any | Optional extra payload attached to specific errors |
Common errors at a glance¶
What a customer is most likely to hit, and where to go next:
errorDescription they'll quote |
HTTP | What it actually means |
|---|---|---|
| "Your package has been expired." | 400 | The organization's package lapsed — blocks sending and signing for everyone in it. See Billing |
| "Insufficient credit balance to perform this action." | 400 | The organization is out of credits |
| "Electronic/Advanced/Qualified signature quota exceeded." | 400 | That user's personal quota, not the organization's — a colleague may still be able to sign |
| "Your service plan is currently inactive…" | 500 | A billing state, not an outage, despite the 500 |
| Link/code expired | 400 | Signing link is past its configured lifetime. Links can't be extended — re-dispatch to that recipient |
| OTP invalid / expired / throttled | 400 / 400 / 429 | Identity verification: 6 digits, 10-minute lifetime, 5 attempts, 30-second resend throttle |
| Identity not verified | 403 | Must complete the OTP challenge before signing — see Guest Signing |
| Invalid document type | 400 | Not pdf/doc/docx — or replacing an existing file with a non-PDF |
| Invalid workflow status | 400 | Recipients and fields are frozen once sent; only DRAFT is editable |
| Session expired | 401 | Re-login required, not a fault — see below |
Validation errors¶
A failed request-body validation returns HTTP 400:
A malformed JSON body — unparseable, or a field with the wrong type — returns:
This one specifically means the type was wrong (a string where a number belonged, an unrecognized enum value), not that the value was out of range.
Worked examples¶
What the full exchange looks like for the errors that come up most.
Package expired on send¶
POST /workflow/v1/887/commence
Content-Type: application/json
{ "title": "Please sign", "message": "..." }
HTTP/1.1 400 Bad Request
{
"errorCode": 16003,
"errorDescription": "Your package has been expired."
}
Blocks sending and signing across the whole organization — so it presents as "nothing works for anyone here," not as a single stuck document.
Recipient name rejected¶
POST /workflow/v1/887/recipients
Content-Type: application/json
[ { "name": "J", "emailAddress": "jane@example.com", "useDigitalCert": false, "recipientColor": "#4F46E5" } ]
HTTP/1.1 400 Bad Request
{
"errorCode": 11001,
"errorDescription": "name: size must be between 2 and 50"
}
Names also reject digits and punctuation — letters and single interior spaces only, 2–50 characters.
OTP resent too quickly¶
HTTP/1.1 429 Too Many Requests
{
"errorCode": 16008,
"errorDescription": "Please wait before requesting another code."
}
The only 429 on the platform. Wait 30 seconds.
Editing a workflow that's already sent¶
Recipients and fields are frozen once sent — that's what makes the audit trail meaningful. Delegate instead, or void and restart.
Unexpected/unhandled exceptions¶
A small category of unhandled conditions returns HTTP 401 rather than 400 or 500. If a request carrying a valid, unexpired token comes back 401 and clearly isn't an auth problem, it's likely one of these — suspect the payload (a bad reference id, a constraint violation) rather than the credentials.
Genuinely unexpected server errors return a generic internal-error code at HTTP 500.
HTTP status code conventions¶
| Status | Meaning in vScrawl |
|---|---|
200 OK |
Success, with a response body |
201 Created |
Resource created (e.g. document upload) |
202 Accepted |
Async work started — Power Survey dispatch. Also means "TOTP required" on mobile sign-in (details) |
204 No Content |
Success, no response body |
400 Bad Request |
Validation failure, malformed body, or a business-rule rejection (expired package, quota exceeded) |
401 Unauthorized |
Missing/invalid/expired token, or one of the unhandled-exception cases above |
403 Forbidden |
Authenticated but not permitted — usually member-vs-owner, or identity not yet verified |
404 Not Found |
Doesn't exist, or isn't visible to the caller |
409 Conflict |
Duplicate — template name, client id, app name |
410 Gone |
Endpoint deliberately retired |
422 Unprocessable Entity |
Semantically invalid signing input (remote-signing authorization data) |
429 Too Many Requests |
OTP resend throttle — the only place this appears |
500 Internal Server Error |
Unexpected failure — or an inactive service plan, which is a billing state, not an outage |
The one exception¶
The session layer can reject a request before it reaches the rest of the API — in that one case the body doesn't follow the shape above at all:
HTTP/1.1 401 Unauthorized
{ "error": "Session expired, please log in again", "code": "SESSION_EXPIRED" }
Note the different field names (error/code, not errorDescription/errorCode). Any client parsing errors generically must handle both shapes. See Authentication → Session cookie.