Authentication

payitgo has two kinds of credentials:

Credential Used for Who uses it
Secret API key (64 hex characters, a test key or a live key) The payments API under /v1 Your server
Access token (a JWT from sign-in) The dashboard API under /merchant People on your team, through the dashboard

Your shop's backend only ever needs an API key. Access tokens are for the dashboard: team, keys, webhook secret rotation, balances and payouts.

API keys

Sending the key

Send the key on every /v1 request, in either header:

Authorization: Bearer 4f9c2a7e1b8d3f6a0c5e9b2d7a4f1c8e3b6d9a2f5c8e1b4d7a0f3c6e9b2d5a8f
X-API-Key: 4f9c2a7e1b8d3f6a0c5e9b2d7a4f1c8e3b6d9a2f5c8e1b4d7a0f3c6e9b2d5a8f

When both are present, X-API-Key wins. A complete request:

curl -s https://api.payitgo.co/v1/tokens \
  -H "Authorization: Bearer $PAYITGO_KEY"

A key is 64 hex characters. Treat it as an opaque string: it does not say its mode.

Test keys and live keys

The key decides the mode of everything the request does. There is no mode parameter on /v1. You choose the mode when you create a key, and the dashboard shows it as a Test or Live badge next to the key.

Key Mode What it does
Test key Test Test payments, paid by the outcomes you simulate (no real money), the test webhook and its signing secret
Live key Live Real payments on mainnet, the live webhook and its signing secret

To ask payitgo which mode a key is in, call GET /v1/webhook with it: the answer's livemode is the key's mode (Webhooks).

Every payment and every webhook notification carries "livemode": true or false, so your code can always tell which mode it is looking at.

Live mode is off until the owner turns it on

A new account starts in test mode. Until the account owner turns live mode on, every request with the live key is refused:

HTTP/1.1 403 Forbidden

{"error": "live mode is off for this account: the owner can turn it on in the dashboard; until then use a test key"}

The owner turns it on with POST /merchant/live (see Account and team). Turning it off again stops new live payments; payments already open still complete.

Storefront keys

A key can belong to a store (one of your shops, see Stores). A storefront key works like any other key, with these differences:

Read-only keys

A key created with "access": "read" can only read: GET /v1/payments, GET /v1/payments/{id}, GET /v1/tokens and GET /v1/webhook. Anything else answers:

HTTP/1.1 403 Forbidden

{"error": "this api key is read-only: it can only read (GET)"}

Give one to a service that reports on payments (an accounting export, a mobile app's order history) and should never create them.

API key errors

Status Body Cause
401 {"error": "missing api key"} Neither Authorization: Bearer … nor X-API-Key was sent
401 {"error": "invalid api key"} The key is unknown, mistyped or revoked
403 {"error": "live mode is off for this account: …"} A live key while live mode is off
403 {"error": "merchant account is suspended"} payitgo suspended the account
403 {"error": "this api key is read-only: it can only read (GET)"} A read-only key used to create or change something
429 {"error": "rate limit exceeded"} More than 20 requests a second with one key (limits)

Managing keys

Your first test key and live key are issued at signup. Owners and developers can create more (one per environment or service, say) and revoke old ones. These routes take a dashboard access token (see below), not an API key.

The API key object

Field Type Description
id string The key's id. Treat it as an opaque string
merchant_id string Your account's id
livemode boolean false for a test key, true for a live key: the only way to tell them apart
store_id string The store the key belongs to. Absent for an account-wide key
label string Your name for the key (default for the two signup keys)
access string full (everything /v1 offers) or read (read-only)
last4 string The last four characters of the secret, to tell keys apart
created_by string The team member who created it (their id)
created_at string When it was created (RFC 3339, UTC)
last_used_at string When it last authenticated a request. Absent until first used
revoked_at string When it was revoked. Absent while it works

payitgo stores only a hash of the secret. The secret is returned once, when the key is created, and can never be shown again.

Create a key

POST /merchant/api-keys?mode=test · owner or developer

Parameter In Type Required Description
mode query string no test for a test key. Anything else, or nothing, makes a live key
label body string no Your name for the key
store_id body string no A store's id: the key becomes that store's key
access body string no full (the default) or read (read-only)
curl -s 'https://api.payitgo.co/merchant/api-keys?mode=test' \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"label": "staging"}'

201 Created:

{
  "api_key": {
    "id": "5e1a9c3d7b2f40e8a6c4d2b1",
    "merchant_id": "3f8a1c27b94e05d6a2c71e90",
    "livemode": false,
    "label": "staging",
    "access": "full",
    "last4": "c0da",
    "created_by": "9b2e4c71d8a05f3e6c1a7b24",
    "created_at": "2026-10-10T09:02:33.526679611Z"
  },
  "secret": "e919a75364398a449f860aeadddc57fa0502145a4e63959ddb33c417a48dc0da"
}

Store secret on your server now: it is not shown again.

Status Body Cause
400 {"error": "unknown store_id"} store_id is not one of your stores
400 {"error": "access must be \"full\" or \"read\""}
401 {"error": "missing token"} · {"error": "invalid or expired token"} No access token, or an expired one
403 {"error": "insufficient permissions"} Your role is finance or viewer

List keys

GET /merchant/api-keys?mode=test · owner or developer

Lists the keys of one mode, revoked ones included. Without mode=test you get the live keys.

curl -s 'https://api.payitgo.co/merchant/api-keys?mode=test' -H "Authorization: Bearer $ACCESS_TOKEN"

200 OK:

{
  "api_keys": [
    {
      "id": "0e7c5a3f9b1d2846a1f3c5e7",
      "merchant_id": "3f8a1c27b94e05d6a2c71e90",
      "livemode": false,
      "store_id": "6d2a9e4c1b7f30a58e2c4d91",
      "label": "eu-shop",
      "last4": "d05e",
      "created_by": "9b2e4c71d8a05f3e6c1a7b24",
      "created_at": "2026-10-10T08:59:52.982Z",
      "last_used_at": "2026-10-10T08:59:53.054Z"
    },
    {
      "id": "4f2a9c1e7b3d5a08c6e1d7b2",
      "merchant_id": "3f8a1c27b94e05d6a2c71e90",
      "livemode": false,
      "label": "default",
      "last4": "5a8f",
      "created_by": "9b2e4c71d8a05f3e6c1a7b24",
      "created_at": "2026-10-10T08:57:25.904Z",
      "last_used_at": "2026-10-10T08:58:44.128Z"
    }
  ]
}

Revoke a key

DELETE /merchant/api-keys/{id} · owner or developer

The key stops working at once: its next request gets 401 {"error": "invalid api key"}.

curl -s -X DELETE https://api.payitgo.co/merchant/api-keys/5e1a9c3d7b2f40e8a6c4d2b1 -H "Authorization: Bearer $ACCESS_TOKEN"

200 OK:

{"message": "api key revoked"}

An unknown id answers 404 {"error": "not found"}.

Rotating a key: create the new key, deploy it, check that requests succeed, then revoke the old one.

Dashboard access tokens

The /merchant/… routes are for people. Sign in with email and password to get an access token:

POST /merchant/login

Field Type Required Description
email string yes Your email address
password string yes Your password
curl -s https://api.payitgo.co/merchant/login \
  -H 'Content-Type: application/json' \
  -d '{"email": "you@acme.example", "password": "a-long-password"}'

200 OK:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJraW5k…",
  "expires_at": "2026-10-10T09:17:33.92212983Z",
  "role": "owner"
}
Field Description
access_token Send it as Authorization: Bearer <access_token> on /merchant routes
expires_at When it stops working: 15 minutes after it was issued
role Your role in the account: owner, developer, finance or viewer

The response also sets an httpOnly cookie, refresh_token, on the path /merchant/auth. POST /merchant/auth/refresh exchanges it for a new access token (same response as login) for up to 7 days. POST /merchant/auth/logout ends the session ({"message": "logged out"}), and POST /merchant/auth/logout-all ends every session of yours ({"message": "all sessions revoked"}).

Status Body Cause
400 {"error": "email and password are required"} A field is missing
400 {"error": "invalid email address"} email is not an email address
401 {"error": "invalid credentials"} Wrong email or password
401 {"error": "no refresh token"} Refresh without the cookie
401 {"error": "missing token"} A /merchant route without an access token
401 {"error": "invalid or expired token"} An expired or malformed access token: refresh, or sign in again
403 {"error": "insufficient permissions"} Your role may not use this route
403 {"error": "merchant account is suspended"} payitgo suspended the account
429 {"error": "rate limit exceeded"} More than 5 sign-in attempts a second from one IP address

Keep keys secret