Authentication and your first request
How keys, scopes, and plan tiers combine to decide whether a request works — and a first call you can paste into a terminal.
Every request to the warrantini API carries an API key. The key decides three separate things, and when a request is refused it is almost always one of them: which merchant you are, what you may touch (scopes), and what your plan allows (tier).
The base URL
https://api.warrantini.com/api/v1Note the /api before /v1 — it is part of the path, and leaving it out is the most common first mistake. The major version lives in the URL; there is no version header.
Getting a key
Keys are created in the merchant dashboard under Developer, not over the API. Creating one requires an owner or admin role — an agent cannot mint a key, which is what stops someone granting themselves access they do not have.
The plaintext key is shown exactly once, at creation. It is hashed at rest with Argon2id and cannot be retrieved afterwards. If you lose it, rotate it — there is no recovery path, by design.
Live and test keys
wrnt_live_…— your production tenant, real data.wrnt_test_…— a sandbox tenant backed by a separate database branch.
The prefix is the whole signal. There is no ?test=true flag and no separate hostname: the key you present decides which world you are in, so a test key can never accidentally write production data.
Keys are server-side only
A warrantini key authenticates as an entire merchant tenant. It must never reach a browser, a mobile binary, or anything a customer can read — there are no publishable client-side keys today. If you need customer-facing warranty data in a front end, proxy it through your own backend.
Your first request
Set the key as an environment variable rather than pasting it inline, so it stays out of your shell history.
export WARRANTINI_API_KEY="wrnt_test_..."
curl -s https://api.warrantini.com/api/v1/orders \
-H "Authorization: Bearer $WARRANTINI_API_KEY"A success returns a data array and a pagination object, even when there is nothing to show yet:
{
"data": [],
"pagination": { "cursor": null, "has_more": false, "limit": 50 }
}Scopes
Each key is issued with an explicit set of scopes. They are chosen at creation and are not inherited from the role of whoever created the key.
orders:read/orders:writeregistrations:read/registrations:writeclaims:read/claims:writeproducts:read/products:writerules:read— read-only; rules are authored in the dashboard
There are bundles — *:read for everything readable, *:write for everything writable — but any subset is valid. A reporting integration that only ever lists orders should hold orders:read and nothing else. Issue the narrowest key that does the job; a leaked read-only key is an incident, a leaked *:write key is a much worse one.
Tiers
Scopes say what a key is permitted to do. The merchant's plan says what is available at all.
- Starter — read access to everything. No writes. 60 requests/minute.
- Growth — single-record writes: order and product ingestion, registration create/void, claim submit/transition. 300 requests/minute.
- Scale — adds bulk import (
/orders/bulk,/products/bulk). 1,000 requests/minute.
A write attempted on a Starter key fails with 403 and the type .../tier-insufficient — distinct from a scope failure, so you can tell *upgrade the plan* apart from *fix the key* without guessing.
When it does not work
Errors are RFC 7807 problem documents. The type is the stable part — match on it, never on title or detail, which are human-readable and may be reworded.
401.../unauthorized— the key is missing, malformed, or revoked. Check theAuthorization: Bearerprefix first.403.../forbidden— the key is valid but lacks the scope for this route.403.../tier-insufficient— the scope is right; the plan is not.404.../not-found— the resource does not exist or belongs to another tenant. The two are deliberately indistinguishable, so an ID cannot be probed for existence.429.../rate-limited— seeRetry-After.
{
"type": "https://api.warrantyplatform.com/errors/forbidden",
"title": "Forbidden",
"status": 403,
"detail": "This key does not carry the orders:write scope."
}The type URI host is a stable identifier, not a link to fetch — treat it as an opaque string you compare against.
Next
What each API is for if you have not read it — it is the model these endpoints assume. Then Errors, idempotency, pagination, and rate limits before you write anything that runs unattended.