This page is the scope matrix and the triage tree — which credential may call
which endpoint, and how to read the error when one may not.
How many keys are involved, and who holds each one?
Four things, and only two of them are yours to keep secret:
The relationship that matters operationally: one API key maps to exactly one
public key. The
X-API-KEY you send decides which public key your signature is
checked against — so an API key and a private key are a matched pair, and
swapping one without the other fails.
Why does my Integration page show several API keys and public keys? Which one do I use?
Because a credential is scoped to a class of operation, and you hold one per class you use. The row you want is the one whose API Operation Type matches the call you are making. Which class covers which endpoint, exactly:
Two details this table settles:
- Balance inquiry is in both the Payout and the Withdraw scope. It is the one endpoint you can reach with either credential, which is why a balance check never forces you to add a scope.
- Account history is Withdraw-only. A payout credential cannot read it.
I used my payin key on a payout endpoint and got a permission error. Why?
Because scope is enforced per request path, not per merchant. The credential was valid — it simply is not allowed to call that endpoint, and the request is rejected withA0004 Permission denied (HTTP 401).
The distinction to keep in mind while debugging: A0004 means “this key, wrong
endpoint”. It does not mean your key is wrong, your signature is wrong, or your
account lacks the product. Look up the endpoint in the table above, then use the
credential row whose operation type contains it.
Why can’t Withdraw be combined with Payin or Payout? I need both.
Because withdrawal moves your own settled funds out, where payin and payout move a customer’s money in or pay a third party. Keeping withdrawal on its own credential means a key held by your checkout service or your payout service can never be used to move your balance out, and that compromising one does not imply the other. The Portal enforces this rather than warning about it: selecting Withdraw clears Payin and Payout, and selecting either of those clears Withdraw. So if you use withdrawals through the API, plan on at least two credentials — one for your payments traffic and a separate one for withdrawals — and store them in different places in your secret manager, with different access lists. That is the point of the separation; putting both in the same environment file gives it away. Submitting the second credential is the same self-serve flow as the first; see Production Public Keys & API Operation Types.In what order do you check my API key, scope, signature and IP?
This order, and knowing it tells you which error you will see first when more than one thing is wrong:- Is
X-API-KEYpresent? If not →A0001. - Is it a known,
Activecredential? If not →A0001. - Does that credential’s operation type allow this endpoint? If not →
A0004. - Does
X-SIGNATUREverify against that credential’s public key? If not →A0002. - Is the source IP on your allowlist, if you have enabled one? If not →
A0046.
A0002, not A0046. So do not conclude your
allowlist is fine because you have never seen A0046 — you may simply never have
got past the signature check. Fix errors in the order above.
What is the difference between “invalid API key” and “invalid signature”?
They point at different halves of the same pair, and each has its own checklist:
After a key rotation,
A0002 almost always means the X-API-KEY and the signing
private key no longer belong to each other — switch both together, as one change.
Is “merchant status abnormal” or “contract does not allow this operation” an authentication problem?
No. Those are account and agreement states, and they arrive after your credential has already been accepted:
The triage shortcut: any
A0001 / A0002 / A0004 / A0046 is a credential
problem you can fix yourself. A0006, A0010 and A0018 are entitlement
problems that need either a configuration change on Waffo’s side or a different
request. Full list in
Error codes.
Do I have to send the API version header?
Yes, on raw HTTP calls: sendX-API-VERSION: 1.0.0. SDKs manage the header
themselves — do not set or override it there.
One trap worth naming, because it is easy to get wrong from a changelog: the
version numbers you see in the API version history identify documentation and
contract revisions and must not be sent in this header. The header value is
1.0.0. See API overview.
How long after I activate or rotate a key can I use it?
Give it a short while — it is not instant. OnlyActive credentials verify
signatures, and the lookup used on the request path is refreshed on an interval —
so a key that has just become Active may still be rejected for a short window,
and a key that has just stopped being active may still work for the same window.
What follows for a rotation: after the new version becomes Active, wait out
that window before cutting traffic over, and keep the old credential in place
until the new one has verified real traffic. Because more than one version can be
Active at once, you never need a gap where neither works.
The IP allowlist behaves differently and faster — see below.
Can I use one API key across several merchant accounts?
No. Each API key belongs to a single merchant account, and a request authenticated with account A’s credential can only act on account A’s data. Querying an order that belongs to account B fails even though the key itself is perfectly valid. If you operate several merchant IDs, keep one credential set per account and always call with the credential of the account that created the object. Building that routing in from the start is much cheaper than retrofitting it — see Multi-brand and sub-merchant setups.What does the IP allowlist accept, and how quickly does an edit apply?
If 20 entries is not enough, consolidate into CIDR ranges rather than listing
individual hosts — one
/24 replaces up to 254 entries, and a stable range is
also less maintenance than a list that changes every time you scale out.
Enabling it, and the exact Portal fields, are in
Portal Self-Service Security Configuration.
My servers are behind proxies, or have dynamic IPs. What actually gets matched?
The address matched is the first entry in the forwarded-for chain of the request as it reaches Waffo — that is, the originating client address, not the last hop. If that value cannot be parsed as an IPv4 address, the check counts as “could not decide” rather than as a mismatch. That distinction drives the outcome, and there are three enforcement policies:
The Portal exposes only the master on/off switch; the policy itself is set by
Waffo. So if your egress is dynamic, multi-region, or containerised behind
several layers, ask integration support to put you in observe only first,
watch for recorded mismatches, and switch to enforcement once the list is
complete. Turning enforcement on blind is how a correctly-configured integration
takes an outage.
If your egress genuinely cannot be pinned to a set of ranges, the allowlist is
the wrong control for you — it is optional and off by default, and signature
verification is already mandatory on every request.
How do I find out what my own egress IP is?
Not from Waffo — the address Waffo sees is the address your infrastructure presents, and the reliable source for it is your own platform: the NAT gateway or load balancer configuration of your cloud environment, which is also the only place it is guaranteed to stay stable. Two practical routes when you cannot get it from configuration:- Ask your platform team for the egress ranges of the environment your payment service runs in, and allowlist the ranges rather than the addresses you observe. Observed addresses go stale the next time you scale.
- Run in observe-only first as described above, so the mismatches themselves tell you which addresses are in play before anything is blocked.
Is my Portal password encrypted in transit? Can Waffo staff see it?
Your password is encrypted in the browser before it is submitted, using a Waffo public key, on top of TLS — so the form never puts a plaintext password on the wire, not even inside the encrypted channel. The same applies when you set or change a password. On the storage side it is kept only as a one-way hash, so there is no recoverable copy to read: nobody at Waffo can retrieve your password, and a password reset issues you a link rather than telling you the old value. Password rules, expiry, lockout and sign-in history are on Portal Sign-in, Links & Account Status and Merchant Portal Operations.I think my private key may have leaked. What do I do, in order?
Rotate first, investigate second — and remember that a leaked private key lets someone sign requests as you, which is exactly as serious as it sounds.- Generate a new key pair and submit the new public key from the Portal. Your existing credential keeps working while it is reviewed, so this step costs you no downtime.
- When the new version is
Active, read its API key from the Integration page and switch theX-API-KEYand the signing private key together. - Stop using the old credential, and confirm from your own logs that no traffic still carries the old API key.
- Turn on the IP allowlist if you have not already. It is the one control that makes a stolen credential unusable from outside your infrastructure, and it is the reason to have it configured before you need it.
- Review your own handling: the private key must not be in source control, in an image, in a log, or in a browser. The go-live checklist requires it to live in environment configuration or a key management service.
- Tell integration support what happened and when, so activity on the old credential can be reviewed from Waffo’s side.
Can I capture request and response payloads to debug a signature problem?
Yes, and you should — but capture the raw bytes, not a parsed and re-serialised copy, because a round trip through your JSON library is itself one of the top causes of a signature that will not verify. What to log, and what not to:
The go-live checklist makes the second column a requirement rather than advice:
logs must not record full keys or sensitive payment information. A short-lived,
access-controlled debug capture is the right shape for this — not a permanent
log line.