Skip to main content

Authentication

Every Partner API request must carry your api_key in the X-API-Key header. One header, every request.

POST /api/v1/partner/invoices HTTP/1.1
Host: api.apreceiving.com
X-API-Key: sk_XXXXXXXXXXXXXXXXXXXXXXXXXXXX
Content-Type: application/json

The Supplier Access endpoints (/v1/partner/supplier-access/*) are deliberately auth-less, they back the supplier-portal pages your users visit and use a per-flow token in the URL or body instead.

What credentials you hold

After completing the claim flow you have two related but separate secrets:

SecretHeaderWhat it grants
api_keyX-API-KeyAll Partner API access (read + write)
rotation_secretX-Rotation-SecretSelf-rotate this specific key only

Hold both. You will need both together to call the rotate endpoint, and losing either one means generating a new key from scratch (via the supplier portal regen page).

A key looks like sk_ followed by ~28 random characters; the rotation secret looks like rs_ followed by ~28 random characters. Server-side both are stored only as HMAC hashes, salted with a per-environment pepper kept in Secrets Manager. We cannot recover the plaintext.

Where to send requests

Nuntiq runs three environments. All three are reachable on the public internet; what differs is the data they hold and which customer is willing to issue you a key on each.

EnvironmentBase URL
Productionhttps://api.apreceiving.com/api
Testhttps://test-api.apreceiving.com/api
Developmenthttps://dev-api.apreceiving.com/api

All Partner API paths begin with /v1/partner/, so the full URL for invoice submission against production is:

https://api.apreceiving.com/api/v1/partner/invoices

A key issued on one environment is only valid on that environment. The path and request shape are identical across all three; only the host changes. See Environments for the full breakdown.

Failure modes

Every authentication failure returns HTTP 401. The body always carries a human-readable message; some responses also include structured fields you can branch on.

Missing header

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{ "message": "Missing API Key" }

You forgot the X-API-Key header. The fix is on your side, no Nuntiq state has changed.

Unknown key

HTTP/1.1 401 Unauthorized

{ "message": "Invalid API Key" }

The hash does not match any live, non-revoked, non-expired key. Could be: typo, wrong environment, key was revoked, or, you guessed it from training data and we do not have it.

Expired or expired-disabled key

HTTP/1.1 401 Unauthorized

{
"error": "key_expired",
"message": "This API key expired on 2026-08-18. Generate a new key at https://acme.dev.apquery.com/supplier-access/regenerate",
"regenerate_url": "https://acme.dev.apquery.com/supplier-access/regenerate"
}

The key is genuinely yours but its expires_at has passed (or the daily maintenance task has stamped expired_at on it). The response tells you exactly where to send a human to generate a replacement.

This is the path you want your integration logs to make obvious — it is the safety net for partners whose recipients muted every reminder email. See the key_expired error.

Wrong rotation secret (rotate endpoint only)

HTTP/1.1 401 Unauthorized

{ "message": "Invalid credentials" }

The rotate endpoint validates BOTH X-API-Key and X-Rotation-Secret. A vague Invalid credentials here means one (or both) did not match. We deliberately do not tell you which, so an attacker who has stolen one half cannot probe for the other.

You also get this 401 if the :key_id in the URL does not match the key carried in X-API-Key — the rotate endpoint is self-rotate only.

Customer key on a Partner endpoint

HTTP/1.1 403 Forbidden

{ "message": "Customer API keys cannot access partner endpoints" }

The key was real, but it is a Customer API key, not a Partner one. These two surfaces never share auth. (The reverse, a Partner key on a Customer endpoint, returns Partner API keys cannot access customer endpoints.)

Storing keys

Standard secret-management rules apply, with two specifically Nuntiq-flavored notes:

  • Both secrets are issued together but stored in different roles. Your normal request path only needs api_key. The rotation_secret should be locked away in the same vault but loaded only by your rotation job, not by every request handler.
  • Save the issued-at date and expires_interval_days. When you rotate, you can re-use the same interval automatically by omitting it from the request body — but only if you remember what you originally picked. Stash both alongside the key.

What's next