Merchant API IP allowlist
The policy lists the public egress IP addresses of your API servers. It does not list your customer's IP, browser IP, webhook destination, or slip-provider allowlist. Keep the existing API key and HMAC signature; this policy adds a source check and does not change the signed canonical request.
Defaults and CIDRs
- LIVE and TEST each have an independent Merchant-self policy. No LIVE list is copied into TEST, and changing one mode does not change the other.
- The default is
disabled, version0,cidrs: [],updated_at: null. enforcedrequires 1–32 canonical IPv4/IPv6 CIDRs, sorted as strings with no duplicates.disabledrequires an empty list. Use/32for an IPv4 host and/128for an IPv6 host; a bare address is not a CIDR.- Example:
["198.51.100.0/24", "2001:db 8::/32"]. These documentation ranges are illustrative; use your actual server egress networks. - The server rejects
/0, host bits such as198.51.100.1/24, leading-zero IPv4, uppercase or expanded IPv6, zones, and dotted IPv6. Compatible::/96and mapped::ffff/96source literals are unsupported, including::and::1. CIDRs wholly within those ranges are rejected; broader canonical IPv6 networks such as::/64remain permitted.
Admission and source trust
The service checks the authoritative policy at new request admission, then consumes the HMAC nonce in the same transaction only if admission succeeds. Both policy errors below leave that nonce unconsumed and cause no business effect. Continue using a fresh PG-Nonce for each HTTP attempt and retain the same idempotency key for the same intended mutation.
Changing the policy affects later admissions. A request admitted before the change may finish; existing payment instructions and already issued slip capabilities are not canceled by this setting. The slip-upload capability endpoint has its own authentication boundary and does not use this HMAC policy.
Source trust requires qualified direct Cloudflare ingress on the exact API host. The service does not accept X-Forwarded-For, X-Real-IP, an IP in JSON, or caller-supplied Cloudflare headers as proof. Requests forwarded by another Cloudflare Worker, or otherwise lacking proven ingress context, cannot supply a usable source under this profile. They get 503 source_ip_unavailable when the policy is enforced; a disabled policy continues to accept absent context. Do not enable enforcement for a Worker-origin integration expecting header forwarding to establish its original client IP.
| Code | HTTP | Retryable | Action |
|---|---|---|---|
source_ip_not_allowed | 403 | false | Correct the configured network or server egress. |
source_ip_unavailable | 503 | true | Check direct ingress/platform readiness; repeated retries do not make unsupported Worker provenance valid. |
The existing route-specific error envelope is retained. A LIVE unavailable response includes Retry-After: 1; TEST IP-policy errors have no Retry-After.
Manage the policy
These are same-origin Back Office session operations, separate from Merchant API-key/HMAC endpoints. Both require exact Merchant self in the selected mode, the api_ip_allowlist contract and all three capabilities: api_keys:read_metadata, api_keys:create, api_keys:revoke. White Labels and Agents cannot select a descendant Merchant to impersonate it. Existing key metadata and Owner/WL operations keep their current contracts.
GET /v1/bo/api-ip-allowlist
X-PG-BO-Client: 1GET accepts no query parameters; the current cookie principal selects scope and mode. It returns { "data": <policy> }, a quoted-version ETag, and Cache-Control: private, no-store, no-transform.
PUT /v1/bo/api-ip-allowlist
Content-Type: application/json
X-PG-BO-Client: 1
If-Match: "0"
Idempotency-Key: merchant-network-change-001
X-PG-Step-Up-Token: <dedicated-request-bound-assertion>{
"scope_id": "11110000-0000-4000-8000-000000000004",
"state": "enforced",
"cidrs": ["198.51.100.0/24", "2001:db 8::/32"],
"reason_code": "API_EGRESS_UPDATE"
}The body has exactly those four fields and is limited to 8192 UTF-8 bytes. If-Match is required: "0" creates, later writes use the current version (0..2147483646). The idempotency key is 8–200 printable non-space ASCII characters. Success returns {data:{status,replayed,http_status:200,policy, request_digest}} and the policy's new ETag. Each new change increments the version; exact retained replay returns the original receipt after current authority and fresh proof checks. Replays do not create another revision.
For enforced, the server must establish an enrolled API host and a valid trusted ingress profile before proof validation or mutation. If unavailable, PUT returns 503 api_ip_allowlist_enforcement_unavailable with no mutation. Saving disabled remains possible with the same normal authority, CAS and fresh proof requirements, even when ingress readiness is unavailable.
Dedicated fresh authentication
Start with POST /v1/bo/auth/step-up/api-ip-allowlist. Its proof binds api_ip_allowlist.replace, PUT, the exact canonical path, resource type merchant_api_ip_allowlist, Merchant UUID, expected version, selected mode, raw body digest, idempotency-key digest and canonical request digest. The session is rechecked and OIDC requires fresh authentication within 5 minutes.
This is a distinct family: state pgsit1., assertion pgsia1., callback message paygate:api-ip-allowlist-step-up. The browser must verify the exact popup and origin. API-key, approval and other step-up assertions are not interchangeable. A changed body/version/mode requires a new proof.
For a version conflict, read the current policy and review the change before a new mutation. Keep a request's raw body/idempotency identity stable for an uncertain-outcome retry, while obtaining fresh authentication as required.
