API keys
API keys let a server or script use a premium account without a session token. A key acts for its account on the inbox, message and paste-pass endpoints, with its own rate limits. This page also covers paste passes, which let a NULL account carry its plan to paste.nullnet.sh.
Using a key
Send the key in the X-API-Key header:
Request with an API key
curl https://mail.nullnet.sh/v1/inboxes \
-H "X-API-Key: grk_Xk3vQ9aB_s8Jd0Lm2Pq4Rt6Vx8Zb1Cd3Ef5Gh7Ij9Kl0Mn2Op4Qr"
- A key is
grk_, an 8-character prefix,_, and a 43-character secret (56 characters in all). Thegrk_<prefix>part is shown in listings so you can tell keys apart. - The full key is shown once, in the create response. The server stores only its SHA-256 hash.
- A key can call: inboxes (
/v1/inboxes…), messages (/v1/messages…,/v1/inboxes/:id/messages…, export) and paste passes. Inboxes created with a key belong to the account. - A key cannot call the session, account, billing, referral, webhook, custom-domain or API-key endpoints, and cannot open the WebSocket. Those return
401 unauthorized. - Requests with a key of a premium account use the
premium_api_keylimits, counted per key. - If a request carries both
AuthorizationandX-API-Key, the bearer token is used.
Scopes
- Name
read- Description
GETandHEADrequests.
- Name
write- Description
Every other method: creating and deleting inboxes, marking messages read, deleting messages, minting paste passes.
A request outside the key's scopes returns 403 forbidden. A key without write can read mail but not change anything.
The API key model
- Name
id- Type
- string
- Description
Key id.
- Name
prefix- Type
- string
- Description
grk_and the 8-character prefix, for display.
- Name
label- Type
- string | null
- Description
Your label.
- Name
scopes- Type
- array
- Description
read,write, or both.
- Name
created_at- Type
- timestamp
- Description
Creation time.
- Name
last_used_at- Type
- timestamp | null
- Description
Last use, to the minute.
Create a key
Creates a key. Needs a logged-in session of a premium account (up to 10 live keys). The body is optional. Returns 201.
400 bad_request:scopesis not a non-empty subset ofreadandwrite.402 payment_required: you are at your key limit. Revoke one first.403 tier_required: your tier has no API keys.
Optional attributes
- Name
label- Type
- string
- Description
Free text, up to 64 characters.
- Name
scopes- Type
- array
- Description
Subset of
read,write. Both when omitted.
Response
- Name
key- Type
- string
- Description
The full key. Store it now.
- Name
api_key- Type
- object
- Description
The key's metadata.
Request
curl https://mail.nullnet.sh/v1/api-keys \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"label": "ci signup tests", "scopes": ["read", "write"]}'
Response
{
"key": "grk_Xk3vQ9aB_s8Jd0Lm2Pq4Rt6Vx8Zb1Cd3Ef5Gh7Ij9Kl0Mn2Op4Qr",
"api_key": {
"id": "019a2f80-6d7e-7f80-91a2-b3c4d5e6f708",
"prefix": "grk_Xk3vQ9aB",
"label": "ci signup tests",
"scopes": ["read", "write"],
"created_at": "2026-10-26T08:00:00Z",
"last_used_at": null
}
}
List keys
Returns the account's live keys as an array. The secret is never returned.
Request
curl https://mail.nullnet.sh/v1/api-keys \
-H "Authorization: Bearer {token}"
Response
[
{
"id": "019a2f80-6d7e-7f80-91a2-b3c4d5e6f708",
"prefix": "grk_Xk3vQ9aB",
"label": "ci signup tests",
"scopes": ["read", "write"],
"created_at": "2026-10-26T08:00:00Z",
"last_used_at": "2026-10-26T08:12:00Z"
}
]
Revoke a key
Revokes the key. It stops working at once. Returns 204, or 404 if there is no such live key.
Request
curl -X DELETE https://mail.nullnet.sh/v1/api-keys/019a2f80-6d7e-7f80-91a2-b3c4d5e6f708 \
-H "Authorization: Bearer {token}"
Paste passes
A paste pass is a short-lived signed token that tells NULL paste (paste.nullnet.sh) what a logged-in NULL account may do there. The mail API signs it with Ed25519. The paste service checks it offline against a pinned public key and applies the limits it carries. A pass grants nothing on the mail API itself. How the paste service uses it is described in Paste premium.
- The token has the form
np1.<payload>.<signature>(unpadded base64url) and is sent to the paste API in theX-Null-Passheader. - A pass is valid for one hour. It cannot be revoked; the short lifetime is the bound. Mint a new one when it expires.
- The pass carries
tier(freeorpremium), a per-account pseudonymsubthat does not reveal the account, and the paste limits of your tier:max_size_bytes,max_ttl_secs,max_files,live_storage_bytes,uploads_per_minand a list of enabledfeatures(dashboard,open_alerts,file_requests). - A lapsed premium subscription simply yields a free pass.
The current paste limits per tier:
| Limit | Free account | Premium |
|---|---|---|
| Largest paste or upload | 1 MiB | 100 MiB |
| Longest expiry | 24 hours | 30 days |
| Files per link | 1 | 20 |
| Live storage per account | none | 2 GiB |
| Uploads per minute | 10 | 60 |
| Features | none | dashboard, open_alerts, file_requests |
Mint a paste pass
Mints a pass for the calling account. Empty body. Authenticate with a logged-in session token, or with an API key that has the write scope.
401 unauthorized: no credentials, an anonymous session, or a bad token.403 tier_required: your tier has no paste limits.403 forbidden: an API key without thewritescope.429 rate_limited: the normal per-tier request limit.503 unavailable: passes are not configured on the server.
Response
- Name
pass- Type
- string
- Description
np1.…. Send it to the paste API asX-Null-Pass.
- Name
expires_at- Type
- integer
- Description
Pass expiry, Unix seconds.
Request
curl -X POST https://mail.nullnet.sh/v1/passes/paste \
-H "Authorization: Bearer {token}"
Response
{
"pass": "np1.eyJ2IjoxLCJraWQiOiI3YTVmYmIzYWVmM2MwN2ZiIiwidGllciI6InByZW1pdW0i….tN_-Kw1IgUQR67BY6m4Z…",
"expires_at": 1793005200
}
Get the pass public key
The Ed25519 public key that verifies passes. No authentication. Returns 503 unavailable when passes are not configured.
Response
- Name
v- Type
- integer
- Description
Pass format version,
1.
- Name
alg- Type
- string
- Description
Always
Ed25519.
- Name
kid- Type
- string
- Description
Key id: the first 8 bytes of SHA-256 of the public key, as 16 hex characters. Every pass names the
kidit was signed with.
- Name
public_key- Type
- string
- Description
The raw 32-byte public key, unpadded base64url.
Request
curl https://mail.nullnet.sh/v1/passes/public-key
Response
{
"v": 1,
"alg": "Ed25519",
"kid": "7a5fbb3aef3c07fb",
"public_key": "q3q7Bv1_-GqCx8BX5dqJspYFpEVAzcR_3_2lBA3W68Y"
}