Authentication
The API authenticates with a publisher key. A key is a bearer token that starts
with nln_live_. It binds to one site: a key issued for one site can only read or
settle for that site, never another, even if you own both.
Minting a key
Create keys under Settings → API & webhooks. You'll see the token once, at creation, so store it somewhere safe (a secret manager, your deploy env). If you lose it, revoke it and mint a new one; there's no way to recover the original.
The API is a paid-plan capability. A key only works while its site is on a plan
that includes API access (Studio and above). Downgrade the site and its keys stop
working, and every endpoint returns 403 with {"error": "...", "upgrade": true}.
Sending a key
Send it as a bearer token:
Authorization: Bearer nln_live_xxxxxxxxxxxxxxxxxxxxA missing or unrecognized key gets 401. A key for the wrong site gets 403: asking
verify to settle a payment for a resource on a site the key isn't bound to, for
example.
curl https://gate.naulon.app/_naulon/tenants/self \
-H "Authorization: Bearer nln_live_xxxxxxxxxxxxxxxxxxxx"Scopes
Each key carries scopes that decide which endpoints it can reach. Pick the narrowest set that does the job.
| Scope | Unlocks |
|---|---|
tenant.read |
GET /_naulon/quote, POST /_naulon/verify, POST /_naulon/observe, GET /_naulon/tenants/self |
earnings.read.all |
GET /_naulon/earnings and its statements, reconciliation, and export variants |
audit.read |
GET /_naulon/audit |
member.read |
GET /_naulon/members (your author/payee roster) |
domain.manage |
The /_naulon/verify-host writes, proving a runtime owns the domain it enforces on |
A key without the scope an endpoint requires is treated as unauthorized for that endpoint. Scopes are independent of the plan gate above: a key needs both the scope for the route and a plan that includes API access.
domain.manage is the only write scope, and the permission matrix does not offer it.
It comes on the Connect WordPress preset, expires in 30 days, and is dropped from the
key the moment the domain verifies. If you are setting up the plugin, mint that preset
rather than building a key by hand; the reason it works that way is in
In-app enforcement.