Errors
A request that fails is answered with a status other than 200, and a message of one line. The status says what kind of failure it is, and whether to try again; the message says what exactly.
The response
HTTP/1.1 400 invalid json in items[0].total: missing currency
Content-Type: text/plain
invalid json in items[0].total: missing currency
The message is the body, in UTF-8, ended by a newline, and over HTTP/1.1 it is also the status line's reason phrase. Read it from the body, as HTTP/2 has no reason phrase. Log it whole: it names the field and the fault, and it is what we ask for when you write to support.
An error is never JSON. Check the status before you parse the body.
Status codes
| Status | Meaning | Retry |
|---|---|---|
400 |
The request is wrong: its JSON, an amount, a capture or refund without a "total", or an Idempotency-Key. The message says what. |
No. Fix the request. |
401 |
The API key is missing or wrong, or may not make this request. See Authentication. | No. |
404 |
The URL fits no request, or the Authorization header holds no key of the form {shopid}:{secret}. |
No. |
405 |
The URL is right, but not the method, such as a GET to create a link. |
No. |
408 |
The request's body did not arrive within 30 seconds of its headers. | Yes, with the same Idempotency-Key. |
413 |
The body is over 32 KiB, or the request line and headers over 4 KiB. | No. |
414 |
The URL's path has more than 8 segments. | No. |
500 |
Anything else that went wrong: we failed, or a party behind us did, such as the acquirer, or the request did not fit the object, as an unknown transaction, a capture with an old "index", or a declined charge. The message says what. |
After you synchronize, with a new Idempotency-Key, if the act or charge did not happen and the message does not say why it can't. |
502, 503, 504 |
We could not be reached, as while we deploy. If it lasts, our status page says whether we are down. | Yes, with the same Idempotency-Key. |
In v1, a 500 is the answer to most failures, of ours and of yours,
and its message is the only thing that tells them apart. Log it, and
don't branch on it: the messages may change. v2 gives each kind of
failure a status of its own.
A request's own page lists the errors that only it gives:
| Request | Its errors |
|---|---|
| Create payment link | Errors |
| Capture, refund | Capture, refund |
| Create subscriber | Errors |
| Charge subscriber | Charge subscriber |
| Renew subscriber | Errors |
| Seq | Errors |
Invalid JSON
A body that is not JSON, or an amount or an integer that can't be read, is answered with:
400 invalid json in {where}: {why}
{where} is the path of the field at fault: keys joined by dots, with
array indexes in brackets, as in items[1].total or
billing.address[2]. It is json root object when the fault is in the
object as a whole, and json data when there is no object at all.
{why} is one of these, or another that says as plainly what is
wrong:
{why} |
Meaning |
|---|---|
no json present |
The body is empty. |
unexpected end of object |
The body ends inside the object, as when it is cut off. |
invalid amount |
An amount is not a number and a currency. See Amounts. |
missing currency |
An amount has no currency. |
mismatching currencies DKK and EUR |
The items' totals are in more than one currency. |
missing total in item |
An item has no "total". |
number is not a pure integer |
An integer field has a fraction or an exponent. |
illegal control character \x0A in string |
A string contains a raw control character, here a newline, which JSON requires to be escaped. |
invalid utf in string |
A string is not valid UTF-8. |
A field of the wrong type is not among them: v1 ignores it, as Requests says.
A capture, a refund, or a charge whose body has more after the object
than white space fails with 400 invalid json: garbage at offset {n},
where {n} is the byte where the trouble starts. Create payment link
reads the object and ignores what follows it.
Retrying
A request that got no answer, because the connection broke or timed
out, or got a 408, 502, 503, or 504, may or may not have run.
Send it again, unchanged and with the same Idempotency-Key, so that
we answer with the first request's result if it ran. See
Idempotency.
A request that got an error from us, 4xx or 500, has run, and its
answer is stored under its key. Sending it again with the same key gets
the same error, for 24 hours. So:
- A
4xx, but408, fails the same way until you change the request: fix it. - A
500from a capture, a refund, or a charge may have done its work before it failed, as when the acquirer acted and our database did not, or may have failed because the transaction changed, or the bank declined the charge. Synchronize, and if the act or charge is not in seq, read the message to decide: send it again with a new key, now or later, or not at all.
Between tries, wait, longer each time: from 1 second, doubling, to a minute. The implementation note on retries works through the cases, such as a capture whose answer never came.