This page is not translated yet and is shown in English.

API keys

Creating, labelling, rotating and revoking a merchant API key, where it may and may not live, and the three routes that need one.

Last updated: 2026-09-04

A merchant API key is the single credential the verification API accepts. Of the three routes that need one, two create and read verification sessions and the third reads your subscription status; the key is the only thing standing between a caller and billable usage on your account.

What a key looks like

A key is the string mk_ followed by 48 hexadecimal characters:

mk_3f9c1a7e0b4d8266f15a9c3e7d20b84c1e6f0a95d3b72c48

The API stores only a SHA-256 hash of it. That is why the dashboard shows the full value exactly once, when you create it, and can never show it again — there is nothing left to show. If you lose a key, you rotate it; there is no recovery path.

Creating a key

Create keys from the API keys page of your merchant dashboard. A key takes an optional label of up to 100 characters, purely so you can tell yours apart later — Production, Staging, Recovery. The label is not part of the credential and changing nothing about it changes nothing about access.

You can hold more than one key at a time. Give each integration its own — a WooCommerce store, a Shopify shop and a server-side proxy should not share one — so that revoking a compromised key takes down one integration rather than all of them.

Copy the value straight into your secret store when the dialog appears. Closing that dialog is irreversible.

Where a key may live

Server-side only, with no exceptions

A key authorises starting billable sessions and reading their verdicts. It must never appear in browser JavaScript, a mobile app bundle, a public repository, a support ticket, or a URL query string. Anything a shopper's browser can read, a shopper can copy.

That constraint is what shapes the three integrations. The WooCommerce plugin and the Shopify app both hold the key in server-side settings and call the API from your server, never from the checkout page. The JavaScript widget holds no key at all: it talks to a small proxy on your own domain, and that proxy is what attaches the key.

The three routes that need one

RouteWhat the key authorises
POST /api/verification/initStarting a session, which is the billable action.
GET /api/verification/:sessionId/resultReading the verdict, and only for sessions your own account created.
GET /api/merchant/subscriptionReading your own account's tier and subscription status — no billable action, just an authenticated read.

Send it as the x-merchant-api-key header:

curl -X POST https://api.eidas-pro.com/api/verification/init \
  -H "x-merchant-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "verificationType": "age" }'

Nothing else takes a key, and sending one to a route that does not need it changes nothing. Authentication explains why the shopper-facing routes are deliberately open, and why that is not a gap.

Rotating a key

Rotation in the dashboard is a single action, but it is two things at once: it revokes the existing key and issues a new one carrying the same label. There is no overlap window and no grace period. The moment you rotate, every integration still holding the old value starts answering 401.

That makes the one-click rotation right for a key you believe is compromised, where an outage is preferable to leaving it live. For a planned rotation with no downtime, do it in three steps instead:

  1. Create a second key with a new label.
  2. Deploy it to the integration and confirm the integration is working on it.
  3. Revoke the old key from the dashboard.

Revocation also takes effect immediately, so leave the old key alive only as long as it takes to be sure nothing is still using it.

Revoked, expired and unknown keys

A key can carry an expiry date, in which case it stops working when that date passes without anyone doing anything.

Revoked, expired and never-issued keys are indistinguishable from the outside. All three answer the same 401, byte for byte, as sending no key at all:

{
  "error": {
    "message": "invalid_merchant_key",
    "code": "INVALID_MERCHANT_KEY",
    "statusCode": 401
  }
}

That is a deliberate property, not a missing detail — it stops the endpoint being used to discover which keys exist. It also means a 401 cannot tell you which thing is wrong, so when you are debugging, check the header name before you suspect the key. Troubleshooting walks through the cases.

A valid key is not the whole story

Authentication and authorisation are separate. A genuine, active key still cannot start a session if the account behind it has no active subscription, or has a subscription that does not include the verification layer being used — those answer 402 and 403 respectively, not 401. See billing for what each of those means and how to clear it.

Separately, holding a key tells you nothing about which verification methods a deployment can actually serve. That answer comes from GET /api/configsupportedMethods, which needs no key at all and which you should read before assuming any method is available.