Skip to main content

Errors

Every Ha Tang API, from Email to Payments to the chat gateway, reports errors in the same JSON shape with a conventional HTTP status. This page explains that shape, lists the errors you are most likely to meet with what to do about each, and shows how to handle them in code. The error reference lists every code.

The error envelope​

A failed request answers with a 4xx or 5xx status and a body like this:

{
"statusCode": 422,
"name": "validation_error",
"message": "to: Invalid email address"
}
FieldTypeMeaning
statusCodenumberThe HTTP status, repeated in the body
namestringA stable, machine-readable code. Branch on this.
messagestringAn English sentence for people and logs. It may change at any time; do not parse it.

For validation errors, message names the first field that failed, in the form field: problem.

Status codes​

StatusMeaningRetry?
400The request conflicts with an earlier one (idempotency)No, fix the request
401No API key, or the key is not allowed to do thisNo
403The key is invalid, the module is off, or the action is not allowed in this stateNo
404The resource does not exist, or belongs to another business or environmentNo
409Conflict with the current state (a duplicate, a closed conversation, a concurrent request)Sometimes, after reading the current state
413The uploaded file is too largeNo
422The request body or query is invalidNo, fix the request
429Rate limit or daily quota reachedYes, after a delay (rate limit only)
500, 503Something failed on our side or at a providerYes, with backoff

Common errors​

nameStatusWhat it meansWhat to do
validation_error422A field is missing or invalid, or the body is not valid JSONRead message, fix the request. Do not retry as is.
validation_error403Email only: the from domain is not verifiedVerify the domain
missing_api_key401No Authorization: Bearer headerSend the key
invalid_api_key403The key is malformed, unknown or revokedCheck the key; create a new one if it was revoked
restricted_api_key401The key lacks the scope, or its domain or origin restriction forbids thisUse a key with the right scopes
product_disabled403The module is turned off for the businessAsk an owner or admin to turn it on; do not retry
environment_mismatch403A Payments action or key does not match the environmentCheck Hatang-Environment and the key
not_found404No such resource for this business (and environment)Check the id; it may belong to another business or environment
invalid_idempotent_request400The Idempotency-Key was used before with a different bodyUse a new key for a new request (Idempotency)
concurrent_idempotent_requests409Two requests with the same Idempotency-Key ran at the same timeRetry after a moment; you get the first result
rate_limit_exceeded429Too many requests per second for this moduleWait retry-after seconds and retry (Rate limits)
daily_quota_exceeded429The business reached its daily email quotaWait for midnight UTC or raise the quota
conversation_closed409The chat conversation is closedReopen it, or start a new conversation
file_too_large, unsupported_file_type413, 422An uploaded file is too big or of a type that is not acceptedCheck the limits
invalid_state409The object cannot do that now, such as canceling a paid paymentFetch the object and check its status
application_error500 or 403An unexpected failure; also 403 when email sending is paused for the businessRetry a 500 with backoff; for 403, resume sending in Email → Email settings
internal_server_error500Something broke on our sideRetry with backoff; contact support if it persists
provider_unavailable503A bank or e-invoice provider did not answerRetry later

Handle errors in code​

Decide what to do from name and statusCode: retry what is temporary, surface what a person must fix, and log the rest with the request.

The Node SDK never throws for API errors. Every method resolves to { data, error }, where error has the same statusCode, name and message. When the API cannot be reached at all, error.statusCode is 0 and error.name is application_error.

import { Hatang } from "@hatang/sdk";

const hatang = new Hatang(process.env.HATANG_API_KEY, { baseUrl: "https://api.hatang.dev" });

const { data, error } = await hatang.emails.send({ from, to, subject, html }, { idempotencyKey: orderId });

if (error) {
switch (error.name) {
case "validation_error":
// Our bug or bad input: log it and fix the payload.
logger.error({ error }, "Hatang rejected the email");
break;
case "rate_limit_exceeded":
case "internal_server_error":
case "application_error":
// Temporary: retry later with the same idempotency key.
await queue.retryLater({ orderId });
break;
case "product_disabled":
case "restricted_api_key":
case "invalid_api_key":
// Configuration: alert a human, do not retry.
await alertOps(error.message);
break;
default:
logger.error({ error }, "Unexpected Hatang error");
}
}
Find failed calls

Email → Logs in the dashboard lists every API request of the business with its status, duration and the request and response bodies. Look for the calls with a 4xx or 5xx status to see the exact error each one returned.

Next steps​

View this page as Markdown