Skip to contentSkip to navigation
Hatcel
Developers
2026-10-08API status

Security

How the Hatcel API protects your venue's data

A key reads a whole workspace, so the API is built on the assumption that something will go wrong - a key leaks, an agent misbehaves, a request is forged - and that when it does, the damage must stop at one venue and be visible and reversible. This page sets out every safeguard, and what we ask of you.

In short

  • A key can only ever reach the workspace that made it. The workspace comes from the key, never from anything in the request.
  • Every read and write runs inside the database's row-level security, as a database role that can touch only the tables and columns the API serves.
  • Keys are shown once and stored only as a fingerprint. Nobody at Hatcel can read one back.
  • No key can move money, give marketing consent, or change a customer's email or phone once they have one.
  • Every change a key makes is kept with what it replaced, and can be undone.
  • Browsers cannot call the API at all. It is server to server, over TLS only.

The edge and the network

Requests reach Hatcel through Vercel's edge network. Every connection is HTTPS, over TLS 1.2 or 1.3, with ciphers that all offer forward secrecy. A request sent over plain HTTP is redirected to HTTPS - but by then its key has already crossed the network unencrypted, so always call https:// and treat a key sent over HTTP as leaked.

Every response carries Strict-Transport-Security: max-age=63072000; includeSubDomains; preload, so a client that honours it refuses plain HTTP to Hatcel for two years.

  • One door. The API answers only at api.hatcel.com. The same path on any other Hatcel address, or on a venue's own domain, is a 404.
  • No cookies. A request to the API is authenticated by its key alone. It never reads, refreshes or sets a session.
  • No browsers. The API sends no CORS headers, so a browser's preflight fails and a web page cannot call it. See No browser calls.
  • Guessing is throttled. Refused keys are counted per IPv4 address and per IPv6 /64, so rotating through one network's addresses does not buy more guesses. See Guessing is throttled.
  • Never cached, never indexed. Every answer carries Cache-Control: no-store, X-Content-Type-Options: nosniff and X-Robots-Tag: noindex, nofollow.

How we stop anyone reaching your data

Every request passes the same chain, in this order. Each link refuses on its own, so a mistake in one is caught by the next.

  1. The key is looked up by its fingerprint. A header that is not shaped like a key is refused before the database is asked. An unknown, revoked, expired or removed key gets the same 401, so nobody can learn which keys exist.
  2. The key becomes its own member of the workspace. Each key is a member that can never sign in. For each request, the API signs a token for that member alone, valid for 60 seconds, with an ES256 key only the API holds. Revoking a key removes its member, so even a token signed a moment earlier reads nothing.
  3. The workspace comes from the key. No header, parameter or body field can name a workspace. A key cannot ask for another venue's data because there is nowhere to ask.
  4. Row-level security decides every row. The database checks every read and write against the member's workspace and permissions, on every table, with default deny. The API reads your data the way a signed-in member of staff would, never as a privileged account.
  5. A dedicated database role with the least it needs. API requests run as a database role of their own, api_key_client, which is granted only the tables and columns the API's routes actually read and write. That list is generated from the routes themselves and checked automatically before every change ships, so a column the API stopped using loses its grant. Each statement is cut off after 3 seconds.
  6. The permission is asked twice. The API checks the key's access before running an endpoint, and the database asks again. A read only key's write is refused at both.
  7. Test keys are fenced in the database. Restrictive policies, which no other rule can widen, stop a test key touching a real customer.

The one request that runs as a privileged role is DELETE /test-data, which only a test key may call, and which removes only the rows the workspace's own test keys made.

A key cannot be made while Hatcel support is signed in as a member of your team. A key made that way would outlive the session and act for your venue with nobody's name on it.

How keys are kept

  • Shown once. A key is shown when it is made and never again. Hatcel stores only its SHA-256 fingerprint, so nobody at Hatcel, and nobody who reads our database, can recover it. A key is 40 random letters and digits after its prefix, so a fast hash is the right one.
  • It says what it is. The prefix names its mode - hat_live_ or hat_test_ - so a pasted key can be recognised, by you and by secret scanners.
  • Shown short afterwards. The Developers screen shows the prefix and four characters, never enough to use.
  • Its choices never change. Live or test, and read only or read and write, are chosen once. A different answer is a new key.
  • Read only by default. Writes are an explicit choice, and a read and write key can change only customers, their tags and their lists.
  • Revoked, never deleted. A revoked key stops working on the next request and its history stays: its requests, its changes and who made it.
  • Capped. A workspace holds at most 25 keys and can make at most 20 in an hour.

See API keys for choosing one, and Authentication for sending it.

Keys nobody is watching

  • You hear when a key is made or revoked. Everyone who may revoke keys in your workspace is emailed, with who did it, when, and a link straight to the key.
  • Idle keys are revoked. A key unused for 90 days is revoked automatically. Whoever may revoke keys is warned 14 days before, and told again when it happens.
  • A departed maker is flagged. A key whose maker has left your team is marked Needs review on the Developers screen.

No browser calls

The API sends no Access-Control-Allow-Origin header, ever. A key in a web page is a key in every visitor's hands, so a browser's preflight fails and the call never leaves the page. Call the API from your server.

Rate limits and budgets

Limits are there to stop a runaway script or a stolen key doing damage at speed. Each is counted separately:

  • Requests per key, a minute and a day.
  • Requests per workspace, across all its keys.
  • Writes per key, a minute and a day - counted by HTTP method, so no kind of change slips past.
  • Removals per key a day, because a removal is the change nobody sees happen.
  • Refused keys per address, so guessing is throttled.

The figures are on Rate limits. Every answer says where you stand in RateLimit and RateLimit-Policy headers.

Writes that cannot go wrong twice

  • Idempotency. Every POST must send an Idempotency-Key. A retry with the same key gets the first answer instead of making a second customer; the same key with a different body is refused. See Idempotency.
  • Optimistic concurrency. Every record read carries an ETag. Send it back as If-Match and the change is refused with a 412 if the record moved on since you read it. The check is made inside the database write itself, so nothing can land in between.

Strict input

  • Unknown fields are refused, in a body and in a query string. A typo is a 400, never silently read as "unchanged" or "everything".
  • JSON only, and small. A body must be application/json and at most 64 KB.
  • Validated by the same rules as the back office. A value the venue's own staff could not save, the API cannot save either.
  • Cursors are checked. A page cursor is decoded and validated before it touches a query.

Errors say nothing internal

Every refusal is a problem body from a closed list of codes. An unexpected failure is a 500 with a request id and nothing else - no stack, no table, no column. We see the cause under the same id, so quoting it is all we need. An unknown address gets the API's own JSON 404, so a prober learns nothing about what runs behind it. See Errors.

What no key can do

  • Move money. Payments, refunds, orders and gift card balances are read only, enforced by the database as well as the API.
  • Give marketing consent. marketing_consent: true is refused. Consent is the customer's own act, so a key can only withdraw it.
  • Change a customer's email or phone. Filling an empty one is fine; changing or clearing one is refused. The email is the key to the customer's portal, and changing it would hand a stranger their bookings.
  • Manage keys. No key can list, make or revoke API keys. It can ask only about itself, through GET /key.
  • Reach real customers from a test key. A test key reads and writes test data only, held by the database rather than by code alone. See Test data.

Every change is kept, and can be undone

Every customer, tag and list change any key makes is recorded by the database itself, with the request id, the key, and what the record held before and after. It is kept for a year. No key can read, write or erase it.

On a key's page in Settings > Developers, someone who manages keys can pick one change, several, or a whole day's, and undo them. An undo puts back only what the change replaced, and never overwrites an edit made since. Marketing consent a key withdrew is never turned back on.

What we record about each request

Every request made with a known key is recorded: the key, the request id, the method, the path, the status and error code, how long it took, the IP address and the user agent. The key itself, the headers, the query string and the body are never recorded.

RequestKept for
Reads (GET)30 days
Changes (POST, PUT, PATCH, DELETE)365 days

A key's page in Settings > Developers shows its recent requests, so you can see what it has been doing.

Protocols and standards

WhatStandard
TransportHTTPS, TLS 1.2 and 1.3, HSTS
AuthenticationBearer token in the Authorization header (RFC 6750)
Token to the databaseJSON Web Token (RFC 7519), signed ES256 (RFC 7518), valid 60 seconds
Stored keysSHA-256 fingerprint
ErrorsProblem details, application/problem+json (RFC 9457)
Rate limitsRateLimit and RateLimit-Policy headers (IETF draft)
RetriesIdempotency-Key header (IETF draft)
ConcurrencyETag and If-Match, 412 (RFC 9110)
PagesOpaque cursors, never offsets. See Pagination
DescriptionOpenAPI 3.1

Your responsibilities

Most of what keeps your data safe is ours to do. These parts are yours.

  • Keep keys on a server. In an environment variable or a secret store - never in a browser, a mobile app, a URL, a repository or a chat with an AI agent.
  • One key per integration, with the least access it needs. Read only unless the job writes. Then revoking one stops exactly one thing.
  • Test first. Build against a test key and test data before a live key sees a real customer.
  • An agent you build yourself reads only unless the job is organising customers, with its own key and a person reviewing each change.
  • Rotate and revoke. Replace a key when someone who held it leaves, and revoke any key you no longer use. Making the new one before revoking the old means no downtime.
  • Act fast on a leak. If a key may have been exposed, revoke it in Settings > Developers straight away, then tell us at nigel@hatcel.com.
  • Protect what you take out. Data the API returns is your venue's responsibility once it leaves Hatcel. See Data and privacy.

Reporting a vulnerability

If you think you have found a security weakness in the API or the platform, email nigel@hatcel.com with what you found and how to reproduce it. Please do not access, change or keep anyone else's data, and do not scan or load test the API - see Acceptable use. We will reply, keep you informed, and fix what is real.