Tokens and HTTP delivery
Fetch Octroi releases over HTTP with a bearer token: the newest release, its files, the signing key, caching, and every error code.
Releases are served over HTTP. You ask which release is newest, fetch its files, check them, and keep them: a release never changes, so you fetch each one once.
Tokens
Octroi issues tokens on request, with the licence. A token looks like tdk_live_ followed by 64 hex characters.
- It is shown once, when it is issued. Octroi keeps only its SHA-256 hash, so a lost token can't be shown again; ask for a new one.
- It is licensed either for countries named when it is issued, or for
all, which covers every country in every release, including countries a later release adds. - It expires, at most 366 days after it is issued, and Octroi can revoke it before then.
Send it on every release request as a bearer token:
GET /tax-data/v1/releases/latest HTTP/1.1
Authorization: Bearer tdk_live_…Keep it on your server. Don't ship it to a browser.
Base URL
The base URL is https://api.octroi.app. The paths below are relative to it. The samples read the token from OCTROI_TOKEN.
Routes
| Route | Token | Returns |
|---|---|---|
GET /tax-data/v1/releases/latest | Required | The newest release, the files the token can fetch from it, and when the token expires. |
GET /tax-data/v1/releases/<id>/<path> | Required | One file of a release. |
GET /tax-data/v1/signing-key | None | The public key that verifies the newest release's signature. |
The newest release
GET /tax-data/v1/releases/latest returns JSON, with Cache-Control: private, no-cache:
{
"release": "2026-10-11",
"publishedAt": 1791709200000,
"countries": ["es"],
"files": [
{
"path": "manifest.json",
"sha256": "945133b76a245c7e727b173634affa45c3fdb75370005ba1ec284a5204fc0e8a",
"bytes": 11670
},
{
"path": "countries/es.json",
"sha256": "52d6cbe6076e7090335194357d7a4f61755286a9f2039e91e7311b6ee848e982",
"bytes": 106861
}
],
"token": { "expiresAt": 1823245200000 }
}| Field | Meaning |
|---|---|
release | The newest release's id. |
publishedAt | When it was published, in milliseconds since the Unix epoch. |
countries | The countries in it that the token is licensed for. |
files | Every file in it the token can fetch, each with its path, sha256 and size in bytes. |
token.expiresAt | When the token expires, in milliseconds since the Unix epoch. |
The list is cut to two files here. When release is one you already have, there is nothing to fetch.
A file
GET /tax-data/v1/releases/<id>/<path> returns one file of any release, newest or not, by its path in the release, such as /tax-data/v1/releases/2026-10-11/countries/es.json.
| Header | Value |
|---|---|
Content-Type | application/json for .json, text/markdown; charset=utf-8 for .md, text/plain; charset=utf-8 for .txt, application/octet-stream for the signature. |
ETag | The file's SHA-256 in hex, quoted. |
Cache-Control | private, max-age=31536000, immutable. |
Send the ETag back in If-None-Match and you get 304 Not Modified with no body. Since files never change, a file you have already checked never needs fetching again.
The signing key
GET /tax-data/v1/signing-key returns the newest release's Ed25519 public key as an SPKI PEM, with Content-Type: application/x-pem-file and Cache-Control: public, max-age=3600. It needs no token. Before the first release it is a 404, release_not_found. Verify a release shows what to do with it.
What a token can fetch
A token can fetch, from any release:
- every file that belongs to no country:
manifest.json,manifest.json.sig,LICENSE.txt,changes.json,CHANGELOG.mdand the schemas; countries/<cc>.jsonandvectors/<cc>.jsonfor each country it is licensed for, or for every country with analltoken.
Any other country's file is a 403, country_not_licensed. The manifest still lists every country, so you can see what a release holds.
Errors
Errors are application/problem+json, with Cache-Control: no-store:
{ "status": 403, "code": "country_not_licensed" }Error codes
| Status | Code | Meaning |
|---|---|---|
| 401 | token_missing | No bearer token in the Authorization header. |
| 401 | token_invalid | No such token. |
| 401 | token_revoked | The token has been revoked. |
| 401 | token_expired | The token has expired. |
| 403 | country_not_licensed | The file belongs to a country the token isn't licensed for. |
| 404 | release_not_found | No such release, or, for latest and the signing key, no release yet. |
| 404 | file_not_found | No such file in the release, or a release id or path that isn't valid. |
A 401 also carries WWW-Authenticate: Bearer.
Fetch the newest release
This fetches every file the token can read into ./releases/<id>/ and checks each against the hash latest gives. sha256Hex is from Verify a release.
import { mkdir, writeFile } from "node:fs/promises";
import { dirname } from "node:path";
import { sha256Hex } from "./verify";
const base = "https://api.octroi.app";
const headers = { Authorization: `Bearer ${process.env.OCTROI_TOKEN}` };
type Latest = {
release: string;
publishedAt: number;
countries: string[];
files: { path: string; sha256: string; bytes: number }[];
token: { expiresAt: number };
};
async function get(path: string): Promise<Response> {
const response = await fetch(`${base}${path}`, { headers });
if (!response.ok) {
const { code } = (await response.json()) as { code: string };
throw new Error(`${response.status} ${code} for ${path}`);
}
return response;
}
const latest = (await (await get("/tax-data/v1/releases/latest")).json()) as Latest;
const dir = `./releases/${latest.release}`;
for (const file of latest.files) {
const url = `/tax-data/v1/releases/${latest.release}/${file.path}`;
const bytes = new Uint8Array(await (await get(url)).arrayBuffer());
if ((await sha256Hex(bytes)) !== file.sha256)
throw new Error(`${file.path} does not match its sha256`);
await mkdir(dirname(`${dir}/${file.path}`), { recursive: true });
await writeFile(`${dir}/${file.path}`, bytes);
}The hashes in latest catch a broken download; they are not signed. Before you use the files, check the signature and the hashes in the signed manifest.
The same with curl:
curl -fsS -H "Authorization: Bearer $OCTROI_TOKEN" \
"https://api.octroi.app/tax-data/v1/releases/latest"
curl -fsS -H "Authorization: Bearer $OCTROI_TOKEN" \
"https://api.octroi.app/tax-data/v1/releases/2026-10-11/manifest.json" -o manifest.json