Skip to content

Authentication

Every request carries a credential:

Authorization: Bearer emv_...

There is nothing else to send. No signature, no timestamp, no second header.

Keys are made and revoked in your account, at emvero.io/account/api-keys/. Give the key a name - it is for you, not for us; something that says where it is used - and the token is shown once.

Once is literal. What the service stores is a digest, so nobody, including us, can recover the token afterwards. What the account area keeps beside it is a name, the first few characters, and when it was made - enough to tell two keys apart in a list and not enough to use one. A lost key is revoked and replaced, never looked up.

Two conditions on making one:

  • The address on the account has to be proven. An unconfirmed account can sign in and see its own state, but not mint a credential - that is the path by which somebody signing up with a stranger’s address would otherwise end up holding a working key. The confirmation link already in the inbox clears it.
  • Ten live keys per account. Revoked ones do not count against it. Ten is more integrations than anybody has, and the ceiling is what stops a loop over this route becoming unbounded credentials in a tier that has no session to refuse with.

Every key on an account spends the same balance. There is no per-key allowance and nothing one key can do that another cannot.

Revoke a key and it stops being valid immediately - but the tier that checks credentials caches what it resolved for up to five minutes, so a request carrying a revoked key can still be accepted inside that window. Longer if our database is unavailable, because that cache is deliberately what keeps authentication working through an outage.

Plan for it. If a key has leaked, revoking is the right first move and it is not the whole of the response: assume the window, and treat anything that key could have done in five minutes as done.

Revocation is also one-way. Nothing stored the original, so reinstating is not something that could be offered - a replacement is minted instead, and an integration that was cut off finds out that it was.## Why there is no anonymous tier

A verifier that answers unauthenticated questions is a directory-harvesting service for whoever finds it, and no rate limit compensates for that. Anonymous access is not a configurable option, and the public checker on emvero.io is not an exception to it - that page runs behind a session and spends the account’s own balance.

401 means the credential was missing, malformed, unknown or revoked. All four give the same answer, deliberately: which of them it was is not information the caller is entitled to, and distinguishing a revoked credential from an invented one turns the endpoint into an oracle.

A 503 with authentication_unavailable is different in kind - the credential could not be checked. Nothing was decided about it, and the request should be retried. See Errors.

  • One credential per integration, not one per company. Revoking is the only way to cut off a compromised integration, and a shared credential means revoking it cuts off everything.
  • Server side only. A credential that reaches a browser has been published. This is exactly why the checker on emvero.io is served by a tier that holds the credential rather than by the page itself.
  • Revoke per integration, and allow for the window. Cutting one integration off is one key; it is also not instant, so a key that has leaked needs the five minutes accounted for rather than assumed away.

A credential carries a class that decides who yields when two requests cannot both be served at once: a person waiting on a page outranks a machine holding a connection, which outranks a list being cleaned overnight.

It is a property of the credential and not of the account, so an integration holds a transactional key for its signup flow and a bulk key for its nightly list - bought with the same money at the same rate, the second yielding to the first. It never grants anything: every limit that would refuse a request refuses it either way. See Limits and pacing.