Skip to main content

Authentication

Every call to the REST API at https://api.hatang.dev is authenticated with an API key sent as a Bearer token. This page shows how to create keys, which scopes to give them, how to restrict them, and what the API answers when a key is missing, wrong or not allowed.

Before you start​

You need to be an owner or admin of the business, or a member with Developer access, to create and revoke keys (Team and roles).

Create an API key​

  1. In the dashboard, open Settings → API Keys. Email users also find the same page under Email → API Keys.
  2. Click Create key and give the key a name that says where it runs, such as production-web or erp-sync.
  3. Under What this key can do, tick the scopes it needs. Only the modules that are turned on for the business are listed.
  4. Optionally restrict the key to one Sending domain or to Browser origins (see Restricted keys).
  5. Click Create key and copy the key. It starts with ht_ and is shown only once: Ha Tang stores only a hash of it.

The keys table shows each key's prefix, what it can do and when it was Last used.

Send the key​

Put the key in the Authorization header of every request:

Authorization: Bearer ht_123...
curl "https://api.hatang.dev/emails?limit=1" \
-H "Authorization: Bearer $HATANG_API_KEY"

The same key also works as the SMTP password for smtp.hatang.dev:5870 (STARTTLS), with the username hatang.

Server side only

An API key acts on behalf of your whole business. Keep it in an environment variable or a secret manager on your server. Never put it in browser JavaScript, a mobile app or a Git repository. Browsers and apps use the chat widget and the mobile SDKs, which have their own public site id and app keys.

Scopes​

A scope has the form <product>:<verb>, <product>:* or *.

ModuleScopesWhat they allow
Emailemail:sendSend email: POST /emails, POST /emails/batch and the SMTP relay
email:readList and fetch emails, metrics and received mail
email:writeManage domains, templates, contacts, audiences, segments, topics, broadcasts and automations
Supportchat:read, chat:writeConversations, messages, visitors, tickets and widget sites; write also replies, assigns and resolves
Paymentspayments:read, payments:writePayments, transactions, bank connections and e-invoices; write also creates payments and issues invoices
Productsproducts:read, products:writeProducts, categories and orders, including an order's payment link
CRMcrm:read, crm:writeCustomers, custom fields, merging, CSV export and ZNS
Everything*Full access to every module

How scopes combine:

  • write and send both include read. A key with email:send can read the emails it sent.
  • <product>:* covers every verb of that product, and * covers every product.
  • Products and CRM grew out of Support: a key with chat:read, chat:write or chat:* keeps working on the Products and CRM endpoints. New keys can use products:* and crm:* to narrow access.
  • A few endpoints belong to the whole business rather than a module. Managing webhook endpoints, reading API Logs and reading Usage need any write scope; managing API keys through the API needs email:write.
Received mail

Reading received mail (/emails/receiving) needs email:read or email:write granted on the key. The read access that email:send implies is not enough there.

The dashboard offers the scopes of Email, Support and Payments. To create a key with products:*, crm:* or *, call POST /api-keys with a key that has email:write:

curl -X POST https://api.hatang.dev/api-keys \
-H "Authorization: Bearer $HATANG_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "erp-sync", "scopes": ["products:*", "crm:read"] }'
{ "id": "key_123...", "token": "ht_123..." }

A key needs the right scope and its module must be turned on. A call to a module that is off answers 403 product_disabled even with the right scope (Businesses and modules).

Restricted keys​

Two optional restrictions limit the damage if a key leaks.

RestrictionWhere to set itEffect
Sending domainDashboard, or domain_id on POST /api-keysThe key can only send from that verified domain. Sending from another domain fails with 401 restricted_api_key. Only applies to keys with an Email scope.
Browser originsDashboard: Where will this key run? → In the browser, then one origin per line, such as https://shop.vnRequests must carry an Origin header from the list. Anything else, including server requests without an Origin header, fails with 401 restricted_api_key.

Choose On your server (the default) for keys used from your backend; such a key has no origin lock. Choose In the browser only for a key that a browser loads. The error message says when a browser-only key was used without an Origin header.

Test keys and the Payments environment​

Only the Payments module has environments. Every business has a development environment, with a simulated bank and the e-invoice provider's sandbox, and a production environment with real money. Payments data never crosses between them. Email, Support, Products and CRM have no environments.

Pick the environment per request with the Hatang-Environment header:

curl https://api.hatang.dev/payments \
-H "Authorization: Bearer $HATANG_API_KEY" \
-H "Hatang-Environment: development"
KeyNo headerHatang-Environment: developmentHatang-Environment: production
ht_...productiondevelopmentproduction
ht_test_...developmentdevelopment403 environment_mismatch
  • Every response repeats the environment it used in the Hatang-Environment header.
  • Any other header value fails with 422 validation_error.
  • In the Node SDK, pass environment: "development" to the Hatang constructor, or set HATANG_ENVIRONMENT.
  • Keys that start with ht_test_ are development keys from earlier versions of Ha Tang. They can never reach production data. New keys start with ht_ and use the header.

See Payments environments for what differs between the two.

Rotate and revoke keys​

Rotate a key when someone who knew it leaves, when it may have leaked, or on a regular schedule:

  1. Create a new key with the same scopes and restrictions.
  2. Deploy it everywhere the old key was used.
  3. Watch the old key's Last used column until it stops changing.
  4. Click Revoke on the old key, or call DELETE /api-keys/{id}.

A revoked key stops working within a minute. Revoking cannot be undone; create a new key instead.

Errors​

StatusnameWhen
401missing_api_keyNo Authorization: Bearer header
403invalid_api_keyThe key is malformed, unknown or revoked
401restricted_api_keyThe key lacks the scope, is used from an origin it does not allow, or sends from a domain it is not locked to
403product_disabledThe module behind the endpoint is turned off for the business
403environment_mismatchA ht_test_ key asked for production, or a Payments action does not exist in this environment
422validation_errorHatang-Environment is not development or production
{
"statusCode": 401,
"name": "restricted_api_key",
"message": "This API key does not have the payments:write scope"
}

Branch on name, not on message. The message is for people and may change. None of these errors go away by retrying: fix the key, the scope or the module first. See Errors for the full picture.

Next steps​

View this page as Markdown