Skip to content

Authentication

Every request to /v1/image needs an API key, sent as a bearer token:

Authorization: Bearer purlo_xxxxxxxxxxxxxxxxxxxxxxxx

Miss the header (or format it wrong) and you get 401 missing_api_key. Send a key that doesn't exist or has been revoked and you get 401 invalid_api_key.

Keys are shown once, at creation. Purlo stores only a hash of your key — if you lose it, revoke it and issue a new one; the plaintext can't be recovered.

Getting a key

Free tier (no card)

The free tier (1,000 images/month) is self-service via an email-verified magic link — no card, about a minute.

  1. Request a key:
curl -X POST https://api.purlo.dev/v1/signup \
  -H "Content-Type: application/json" \
  -d '{"email": "you@example.com"}'

Always returns 202 { "message": "Check your email..." } (the response is the same whether or not you already have an account). This endpoint is rate-limited per IP — too many attempts return 429 rate_limited.

  1. Click the link in the email. It's single-use and expires (30 minutes by default).

  2. Get your key. The link opens GET /v1/signup/verify?token=..., which returns:

{ "email": "you@example.com", "tier": "free", "apiKey": "purlo_..." }

The key is issued and returned once. Verify errors: invalid_token, token_used, token_expired (all 400) — request a fresh link if needed. If your account already has an active key, apiKey comes back null.

Paid signup is self-service via Stripe Checkout, no manual billing support needed:

  1. Start checkout:
curl -X POST https://api.purlo.dev/v1/billing/checkout \
  -H "Content-Type: application/json" \
  -d '{"tier": "starter", "email": "you@example.com"}'

Returns { "url": "https://checkout.stripe.com/..." }. tier must be one of the tiers configured as purchasable (starter, growth, scale); an unrecognised tier returns 400 invalid_tier.

  1. Complete checkout at the returned URL (Stripe-hosted).

  2. Get your key. On success, Stripe redirects to GET /v1/billing/session/{id}/key, which returns:

{ "email": "you@example.com", "tier": "starter", "apiKey": "purlo_..." }

The key is issued and returned once, on your account's first successful checkout. If your account already has an active key, apiKey comes back null — manage/rotate keys via the customer portal instead of re-issuing.

Managing your subscription

Once you have a key, self-service upgrade, downgrade, cancel, and card updates all go through the Stripe Customer Portal:

curl -X POST https://api.purlo.dev/v1/billing/portal \
  -H "Authorization: Bearer purlo_YOUR_KEY"

Returns { "url": "https://billing.stripe.com/..." } — a portal session for your account. No manual billing support required.

Your tier (and therefore quota) updates automatically when your subscription changes; Stripe's webhook is the source of truth, so it applies whether you manage billing through the portal or Checkout.

Issuing keys yourself (self-hosting)

If you run Purlo, the npm run keys CLI mints keys directly, no email needed:

npm run keys -- create-account you@example.com free
npm run keys -- issue you@example.com   # prints the key once

Using your key

Every call to /v1/image needs it:

curl -X POST https://api.purlo.dev/v1/image \
  -H "Authorization: Bearer purlo_YOUR_KEY" \
  -F 'operations={"resize":{"w":800},"format":"webp"}' \
  -F "image=@photo.jpg"