Skip to content

Keys and origins

Partner keys are invite-only. Tell us the drops you are integrating and the domains you serve from, and we issue a key scoped to exactly that.

The three classes

prefixwhere it livesbounded by
Publishablegm_pub_your browser bundleorigin allow-list + collection scope
Secretgm_live_your server onlycollection scope. Nothing bounds the origin
Sandboxgm_test_browser, test networksorigin + scope + refused on production chains

The prefix is deliberate: a leaked key is identifiable on sight, in a log, a paste or a support thread. Someone asking "why does my key 403 on mainnet" can see the answer in the key itself.

A publishable key is public by construction

It ships in your bundle and anyone can read it. That is fine, and it is why we print one on the API reference.

Secrecy is not what protects it. Three server-side bounds are:

  • Origin — it works only from the domains on its allow-list
  • Collection scope — it reads only the drops it was issued for
  • Chain — a sandbox key is refused on any production chain

Take any one away and it is no longer safe to publish. So keep all three accurate.

gm_live_ never reaches a browser

Public build prefixes inline into the bundle

Anything named NEXT_PUBLIC_*, VITE_*, REACT_APP_* or EXPO_PUBLIC_* is inlined into the client bundle by definition. A secret key there is a full partner credential published to every visitor, and no origin bounds a gm_live_ key.

Server-side: process.env.GRAVEMINT_API_KEY, unprefixed.

The mirror-image mistake is using a gm_pub_ key from your server. Browsers set Origin; servers do not, and a key with an origin list refuses a request that carries none. That is correct behaviour, not a bug — origin binding is the whole point.

Registering origins

Register every origin you serve from:

  • production — https://yourdrop.io
  • www. and apex if both resolve — they are different origins
  • staging — https://staging.yourdrop.io
  • preview deploys — Vercel and Netlify give each build its own hostname. Ask us for a wildcard on your preview domain rather than registering them one at a time.

An origin is scheme + host + port. http:// and https:// are different origins, and so is a non-default port.

Scope

A key is issued for specific drops. Reading another returns 403 COLLECTION_NOT_IN_SCOPE, and the refusal is identical whether or not that drop exists — you learn only that this key cannot read it.

An unscoped key reaches nothing. A partner key with no collections is a configuration error, not a wildcard, and it is treated as one.

Scope covers every route that names a collection, including reads. There is no endpoint that returns another partner's drop.

Rate limits

Published floors, not reserved ceilings — we will not lower them without telling you, because a pinned client cannot be asked to redeploy.

limitscope
reads600 / minper IP
prepare + execute60 / min combinedper key
prepare, per collection30 / 10sper collection, shared with gravemint.io

A mint needs one prepare and one execute from the same bucket, so the sustained ceiling is roughly 30 mints per minute per key. And on a hot drop the per-collection cap binds first, because you are competing with every first-party collector.

If a key leaks

Tell us and we revoke it. Revocation is immediate.

It does not undo mints already made with it — but we can tell you exactly what those were, because every mint records the key that made it.

Rotating is issue-new → deploy → revoke-old, in that order, so you never have a window with no working key.

Sandbox keys

Ask for a gm_test_ key to build against a devnet drop. It is refused on any production chain, so it cannot touch a real mint even by mistake — which is what makes it safe to put a live mint console on a public page, as the API reference does.

SDK pages are generated from the published npm tarballs and cannot drift.