Errors
Every error has the same shape. Five fields, always.
{
"type": "https://daichodo.com/errors/not_ found",
"code": "not_ found",
"message": "No invoice issuer with registration number T8000000000001",
"doc_ url": "https://daichodo.com/docs/errors/#not_ found",
"request_ id": "req_ 01J8XZ9Q7M4KDR"
}
code is the machine-readable identifier — branch on that, not on the status
code. Two different errors share 429, and they need opposite handling.
message is a human-readable explanation and may change without notice. Do
not parse it. type is a stable URI naming the error class. doc_url points
at the section of this page for that exact code. Quote request_id when you
contact us; it identifies the request in our logs.
A sixth field, details, appears on the codes that have one. It is absent
entirely rather than null when there is nothing to put in it, so test for its
presence rather than for a value.
Codes
| HTTP | code | Retry? | What it means |
|---|---|---|---|
| 400 | invalid_number | No | The number is malformed |
| 401 | unauthorized | No | Key missing, wrong, or revoked |
| 403 | plan_required | No | The capability needs a higher plan |
| 404 | not_found | No | Well-formed, but not in the register |
| 422 | invalid_ | No | The request itself is malformed |
| 429 | rate_limited | Yes | Too many calls this minute |
| 429 | quota_exceeded | No | The month's allowance is gone |
| 5xx | internal_error | Yes | Ours |
invalid_number — 400
The number is not a well-formed 法人番号 or 登録番号. message says which check
failed, usually the check digit. Validate before you call and you will not see
this — see validation, which is free and uncounted.
Distinct from not_found; see 400 versus 404.
unauthorized — 401
No key, a key that does not exist, or one that has been revoked. Retrying with the same key returns the same answer. Keys are at app.daichodo.com.
plan_required — 403
The key is valid and its plan does not include this capability. Terminal —
retrying burns quota to reach the same answer. Carries details:
{
"code": "plan_ required",
"message": "point_ in_ time requires the standard plan; this key is on the free plan.",
"details": {
"capability": "point_ in_ time",
"required_ plan": "standard",
"current_ plan": "free",
"upgrade_ url": "https://daichodo.com/pricing"
}
}
Branch on required_plan rather than parsing the sentence. This is what lets an
agent tell its user what to buy instead of retrying.
not_found — 404
The number is well-formed and no such record exists. This is a business answer, not a failure: that party is not a qualified invoice issuer.
A sole trader's record with name: null is not this. That is a found
record — identity fields are stripped at source for individuals, and the dates
are authoritative. See lookup.
invalid_ request — 422
The request itself is malformed — a missing or unparseable parameter, a bad
request body, a date that is not a date. message names the field. Unlike
invalid_number, this is about the shape of the call rather than the value of
the number.
rate_limited — 429
Too many calls this minute. Retryable once the window rolls. Carries
details:
{
"code": "rate_ limited",
"message": "Rate limit of 60 requests per minute exceeded. Retry in 12s.",
"details": { "limit_ per_ minute": 60, "retry_ after": 12 }
}
Honour retry_after rather than guessing a backoff. A Retry-After header
carries the same number for anything that reads headers only.
quota_exceeded — 429
The month's allowance is spent. Not retryable, despite the 429 it shares
with rate_limited — the answer is identical until the month rolls over or the
plan changes. This is the reason to branch on code and not on status.
Only register lookups count. Validation is free and unmetered, checking your own quota does not count, and a lookup that finds nothing does not count either.
internal_error — 5xx
Ours. Retry with exponential backoff. message is deliberately generic; quote
request_id and we can find the request.
400 versus 404
400 means "that is not a number". 404 means "that is a number, and it is not
registered".
They are distinct because your response differs. The first is a bad input. The second is a business answer: that party is not a qualified invoice issuer.
Retrying
Retry 5xx and rate_limited, with exponential backoff, honouring
retry_after where it is given.
Do not retry quota_exceeded, even though it is also a 429. Do not retry
any other 4xx; they return the same thing.