Endpoint reference

POST /api/signup

Get a free API key — 250 requests/month — emailed to the address you submit. Returns { "ok": true }; the key itself never appears in the response body. Re-signing up with the same email rotates the key: the old one is revoked, a fresh one is emailed.

Request

Method: POST. URL: https://pdfops.dev/api/signup. Body: application/json.

FieldTypeRequiredDescription
emailstringYesThe address the key is sent to. Normalized (trimmed, lowercased) before use. One active key per email.

Response

On success: 200 OK, Content-Type: application/json, body is { "ok": true }. The key arrives by email — it is never returned in the body.

Why emailed instead of returned? Two reasons. Ownership proof: anyone can POST any email address, but only the inbox owner receives the key, so delivery doubles as verification — no separate confirm-your-email step. Hygiene: a key in a response body can end up in referrer logs, browser history, CI logs, or shared terminal scrollback; an email keeps it in exactly one place.

On failure: 4xx or 500, Content-Type: application/json, body is { "error": "<code>", "details": "<explanation>" }.

Error codes

Error codeStatusTrigger
invalid_json400Request body wasn't valid JSON.
invalid_email400email missing or didn't match a basic email-shape check.
rate_limited429Signup cap: 5 signups per IP per day. Try again tomorrow.
issue_failed500Key issuance failed. Retryable.
email_failed500The key email couldn't be sent. The just-issued key is revoked before this returns, so a retry starts clean — you'll never have a live key you never received.

Rotation semantics

Example — curl

curl -X POST https://pdfops.dev/api/signup \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com"}'

Example — fetch

const res = await fetch('https://pdfops.dev/api/signup', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'you@example.com' }),
});
const json = await res.json();
// json === { ok: true } on success — now check your inbox for the key.

Once the key arrives, pass it as X-API-Key on POST /api/fill-form and POST /api/merge, and check consumption with GET /api/usage or the dashboard.

FAQ

I signed up but didn't get the email — what now?

First check spam/promotions for a message from hello@pdfops.dev. If the send genuinely failed you'd have received a 500 email_failed, not a 200 — and in that case the key was revoked automatically, so just POST /api/signup again. If you got { "ok": true } and the email never arrived, re-signup is still safe: it rotates to a fresh key and sends a new email. Still stuck? Write hello@pdfops.dev.

I lost my API key — can you re-send it?

No — only the SHA-256 hash of the key is stored, so nobody (including PDFops) can recover the original string. Recovery is re-signup: POST /api/signup with the same email revokes the lost key and emails a fresh one. Update any deployed config with the new key, since the old one starts returning 401 invalid_api_key immediately.

Why is the key emailed instead of returned in the response?

Ownership proof (only the inbox owner receives the key, so delivery doubles as verification) and hygiene (response bodies leak into referrer logs, browser history, CI logs; an email keeps the key in exactly one place).

How do I upgrade to a higher quota?

Subscribe at /pricing — Indie is 4,000 requests/month at $16, Pro is 25,000 at $79, both via Stripe. The upgrade re-tiers your existing key, so the key string in your config doesn't change. Need more than Pro? Reply to your key email or write hello@pdfops.dev.

Can I have multiple keys for one email?

No — one active key per email, by design. If you need separate keys per environment (staging vs production), use separate email addresses (plus-addressing like you+staging@example.com works).