Keys and IPs

The credential has a set of public keys and an IP allowlist. Both follow the same asymmetry: widening access waits 18 hours; narrowing it is immediate. The grace period exists so a human can see an alert if someone took the account and tried to register their own key or IP.

Host: https://client.api.corpx.com. Every call carries the signing headers.

Rotate a key

Each credential accepts more than one active public key, so rotation has no 401 window:

  1. POST /v1/backoffice/tenants/{tenantId}/credentials/{clientId}/public-keys with the new key. The response is status: pending and activeFrom 18 hours ahead.
  2. After those 18h, your server starts signing with the new kid.
  3. DELETE .../public-keys/{kid} retires the old one — immediate.

The API refuses to retire the last usable key (409 last_public_key): that would leave the credential unusable, with no way back.

// POST body
{ "publicKeyPem": process.env.NEW_PUBLIC_KEY_PEM }

publicKeyPem is a -----BEGIN PUBLIC KEY----- (SPKI) block for EC P-256 or RSA ≥ 2048. A private-key PEM is refused with 422 invalid_public_key.

curl -X GET "https://client.api.corpx.com/v1/backoffice/tenants/$TENANT_ID/credentials/$CLIENT_ID/public-keys" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: $TENANT_ID" \
-H "X-Request-Timestamp: $TS" \
-H "X-Content-SHA256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" \
-H "X-Request-Signature: $SIG"

IP allowlist

Every credential declares which IPs it may call from — 1 to 20 CIDRs, required at issuance. A request from outside the list is refused at the edge, before the token (403 ip_not_allowed).

curl "https://client.api.corpx.com/v1/backoffice/tenants/$TENANT_ID/credentials/$CLIENT_ID/ip-allowlist" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: $TENANT_ID" \
-H "X-Request-Timestamp: $TS" \
-H "X-Content-SHA256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" \
-H "X-Request-Signature: $SIG"
{
"clientId": "abc123...",
"ips": ["200.10.20.30/32"],
"pendingIps": ["200.10.20.30/32", "200.10.20.31/32"],
"pendingUntil": "2026-09-20T06:00:00Z"
}

PUT takes the whole set ({"ips": [...]}):

ChangeWhen it applies
Remove an IPImmediate
Add an IP+18h (pendingUntil)

ips is what applies now; pendingIps is the set that takes over when the grace ends. If every entry in the PUT is new, the old list keeps applying in the interval — the intent is to change IPs, not to sit disconnected for 18 hours.

RuleWhy
1 to 20 entries, requiredA credential with no known origin is just another token
0.0.0.0/0 refused (422 ip_allowlist_required)“Any IP” is the same as not declaring
Prefix wider than /24 refused/16 is 65 thousand addresses; the list would stop meaning anything

If your server’s egress IP changed (NAT, new provider), register the new CIDR, wait 18h, then change routing. Until then the old list still applies.

What the holder sees

The bank’s internet banking shows the credential’s keys and IPs. Revoking the whole credential is immediate and invalidates token, keys and allowlist.