This guide covers common issues you may encounter when integrating BlinkPay, along with solutions and debugging strategies.
| Error Code | Meaning | Common Causes | Actions |
|---|---|---|---|
| 400 | Bad Request | Invalid request format, missing required fields, invalid date formats, redirect URI not whitelisted | Check request format, verify all required fields, ensure dates are ISO 8601 with timezone |
| 401 | Unauthorised | Invalid or expired access token | Refresh OAuth token automatically and retry request |
| 403 | Forbidden | Blink Payments: feature not enabled or wrong subscription tier. Blink Data: the consent's state or permissions (BDT409–BDT415), or a flow the bank doesn't offer (BDT401, BDT402) | Blink Payments: verify feature access with support. Blink Data: read the code — see 403 Forbidden |
| 404 | Not Found | Invalid consent ID, payment ID, or resource ID | Verify IDs are correct, check consent/payment hasn't expired |
| 409 | Conflict | Duplicate request, resource conflict | Depends on the code: some mean retry, others poll — see When to Retry |
| 415 | Unsupported Media Type | Invalid payload format or missing Content-Type header | Ensure payload is valid JSON and Content-Type is application/json |
| 422 | Unprocessable Content | Blink Payments: consent expired/revoked, invalid consent state, amount exceeds limits. Blink Data: see the Blink Data error codes page | Create new consent, verify consent status; Blink Payments: check enduring consent limits |
| 429 | Too Many Requests | Too many requests in a short time | Wait for the Retry-After time, then back off — see API Rate Limits |
| 5xx | Server Error | BlinkPay or bank system issue | Retry the request (with the same idempotency-key for a POST), contact support if persistent |
Symptom: All API requests return 401
Causes:
Solutions:
Symptom: Specific endpoints return 403
On both products, a 403 can mean your access token lacks the scope the endpoint needs. Scopes are assigned to your API client by BlinkPay; contact support if one is missing.
Blink Payments: the feature is not enabled for your account, or your subscription tier doesn't include it. Contact support to verify feature access and your subscription.
Blink Data: read the code in the response body; the consent is rejected, revoked, expired, not yet authorised, or lacks the permission the endpoint needs. Create a new consent, or wait for authorisation. The Blink Data error codes page lists them.
Symptom: Consent doesn't progress after redirect
Causes:
Solutions:
AwaitingAuthorisation; in the direct decoupled flow with no callback, every 5 secondsAuthorised or RejectedWhat happens if the customer closes the bank app or browser before finishing? The consent stays AwaitingAuthorisation until the bank's window ends and then becomes Rejected; no money moves and no data is shared. If the customer comes back to your site or app without finishing, don't revoke the consent or treat it as failed: they may still approve it in their banking app. Keep checking until the window ends, then reconcile.
Prevention:
Symptom: The consent reaches a terminal status before the customer authorises it — Rejected if they'd already reached their bank, or GatewayTimeout if they never chose one
Cause: Authorisation windows are set per bank and per flow, and gateway consents have an earlier session cutoff before bank selection. Blink Payments: read the applicable window from redirect_flow.request_timeout and features.decoupled_flow.request_timeout on the metadata endpoint rather than hard-coding a figure. Blink Data: GET /v1/meta has no timeouts; use the per-bank windows instead: 15 minutes for bank selection in the gateway, up to 10 minutes for the redirect flow, and a few minutes for the decoupled flow, varying by bank.
Handling:
Prevention:
Symptom: Consent status becomes 'Rejected'
Common Causes:
Handling:
status parameter gives the kind of outcome and error carries text you can show (see Return Parameters)Important: Never automatically retry rejected consents. Customer needs to resolve the underlying issue first.
Blink Payments only: for quick payments, a rejected consent also appears as a payment record with status Rejected in the consent's payments[] array.
Blink Payments only.
Symptom: Payment doesn't move to AcceptedSettlementInProcess
Expected Timeline: Most payments move within seconds. Poll every second until the payment reaches AcceptedSettlementCompleted or Rejected; if it hasn't within a minute or two, stop and pick it up in your periodic wash-up.
If still Pending after 6 hours:
Symptom: Payment status becomes 'Rejected'
Common Causes:
| Reason | Description | Action |
|---|---|---|
| Insufficient Funds | Account balance too low | Ask customer to add funds or use different account |
| Account Closed | Account no longer active | Use different account |
| Invalid Account | Account number incorrect | Verify account details |
| Payment Blocked | Bank fraud detection or customer block | Contact bank, verify legitimacy |
| Exceeds Limits | Daily/transaction limit exceeded | Split payment or wait for limit reset |
| Technical Error | Bank system issue | Retry or contact support |
Handling:
idempotency-keySymptom: Customer not redirected back after authorisation
How a redirect URI is matched against your registered entries:
| Part | Rule |
|---|---|
| Scheme, host and port | Must match exactly (the host is not case-sensitive) |
| Path | Prefix match: registering /callback also accepts /callback/success, and /callbackfoo, so register a path that ends in a distinct segment |
| Trailing slash | A registered trailing slash is required: registering /callback/ rejects /callback. Register /callback, not /callback/ |
| Query string | Ignored, so you can append your own parameters (e.g. ?order=123) |
| Wildcard | https://*.example.com matches any subdomain but not the bare https://example.com |
| Environment | Production rejects http:// and localhost; sandbox allows them. Custom app schemes (e.g. myapp://callback) are allowed in both |
A URI that matches no entry is rejected with 400 (Blink Payments BP214, Blink Data BDT211). The decoupled flow's callback_url is not checked against this list.
Common Mistakes:
http:// vs https:///callback/ and sending /callbackyourapp.com vs www.yourapp.com — consider using a wildcard entry like https://*.yourapp.com/callbackDebugging:
cid query parameter, which carries the consent ID (see Return Parameters)Symptom: Browser console shows CORS errors
Root Cause: BlinkPay API calls must be made from your backend server, not browser JavaScript
Solution:
Never expose your client credentials or make BlinkPay API calls directly from browser code.
Symptom: Banking app doesn't open or return navigation fails
Solutions:
| Platform | Not supported | Use |
|---|---|---|
| iOS | Embedded WKWebView | SFSafariViewController or ASWebAuthenticationSession |
| Android | Embedded WebView | Chrome Custom Tabs |
Embedded WebViews are not supported: many banks block their pages from being embedded, and a real browser keeps the customer's bank credentials out of your app and shows them the bank's real address.
Debugging:
See Mobile Integration Guide for complete details.
Invalid date format:
"from_timestamp": "2025-01-01"; correct "from_timestamp": "2025-01-01T00:00:00+13:00" (ISO 8601 with timezone)expiration_date_time, start_date_time, end_date_time) and the data filters (from_booking_date_time, to_booking_date_time, from_statement_date_time, to_statement_date_time) need a full date-time with an offset, such as 2026-12-31T23:59:59+13:00; a date alone is rejected. In a query string a raw + decodes to a space, so use Z (2026-09-01T00:00:00Z) or encode it as %2B13:00. A consent's expiration_date_time must not fall on the day you create it (BDT258), and a data filter must fall within the consent's start_date_time to end_date_time window (BDT236)Amount format (Blink Payments):
"100.00" or "100.5", not "100". Send two decimal placesPCR fields (Blink Payments):
particulars is required (1-12 characters); code and reference are optional (up to 12 characters each)- & # ? : _ / , . '. Anything else is rejected, not stripped"particulars": "INV12345", "code": "SALE", "reference": "CUST001"Invalid bank:
bank values: ANZ, ASB, BNZ, Kiwibank, NZHL, Westpac, and PNZ in the sandboxBDT243. A bank your organisation isn't set up for returns 400 BDT213; in the sandbox, use PNZ. A flow the bank doesn't offer returns 403 BDT401 (redirect) or BDT402 (decoupled)Redirect URI not whitelisted:
https://*.example.com/callback) to match multiple subdomains. This requires the Developer or Administrator role — otherwise email support@blinkpay.co.nz with the URI(s) you need registered.| Error Type | Retry? | Notes |
|---|---|---|
| 5xx Server Error | ✅ Yes | Retry with exponential backoff, with the same idempotency-key for a POST |
| 429 Rate Limited | ✅ Yes | Wait at least the Retry-After time, then back off exponentially — see API Rate Limits |
| 401 Unauthorised | ✅ Yes | After getting a new access token |
| 409 BP708, BP711 (Blink Payments) | ✅ Yes | Retry the same request with the same key; the earlier request is still in progress |
| 409 BP715 (Blink Payments) | ✅ Yes | Retry the refund with a new idempotency-key |
| 409 BP703 (Blink Payments) | ❌ No, poll | Poll the payment named in the message instead of resubmitting |
| 409 BP712 (Blink Payments) | ⚠️ Check first | Read the consent's payments before retrying; the other request may already have succeeded |
| 409 BDT704 (Blink Data) | ✅ Yes | Retry the consent creation with the same key once the earlier request has completed |
4xx or 5xx on the first quick-payment GET (Blink Payments) | ✅ Yes | Retry the GET: the outcome is not yet known, so don't treat the payment as failed |
| Other 4xx Client Errors | ❌ No | Fix the request - these indicate request problems |
For 5xx errors, use exponential backoff:
A couple of quick retries rides out a brief network or service blip without keeping your customer waiting. If the request still fails, stop and check the state of the consent or payment later rather than retrying in a loop. Always use idempotency keys when retrying POST requests to prevent duplicates.
Idempotency keys prevent duplicate operations when retrying requests. Send the key in the idempotency-key header. The two products apply it differently.
Blink Payments:
| Scenario | Result |
|---|---|
| Same key + same payload, within 24 hours | Original response returned (safe to retry) |
| Same key + different payload | 409 Conflict error (BP702) |
| Same key, earlier request still in flight | 409 Conflict error (BP708), or BP703 naming the in-flight payment — poll it |
| Same key, earlier payment terminal, replay window expired | 409 Conflict error (BP710) naming that payment and its status; use a fresh key |
Same key after the payment ended Rejected | The rejected payment is returned again (or BP710), so nothing is retried; use a fresh key |
When to reuse a key: reuse the same key only when you don't know whether the request succeeded (a timeout, a network error or a 5xx). For a new attempt after a payment is Rejected, use a new key.
Best practices (Blink Payments):
400)request-id header rather than idempotency-key, so resend the same request-id when retrying oneBlink Data: only POST /v1/consents takes an idempotency key. Send a UUID; BlinkPay remembers it for 24 hours for your organisation. Without a key there is no protection, and a retried request can create a second consent.
| Scenario | Result |
|---|---|
| Same key + same body, within 24 hours | 201 with the original consent_id and redirect_uri; no second consent |
| Same key + different body | 409 Conflict error (BDT701) |
| Same key, earlier request still in progress | 409 Conflict error (BDT704); retry once it completes |
Banks may have scheduled maintenance windows, typically overnight (NZ time).
Blink Payments:
| Flow Type | Behaviour During Maintenance |
|---|---|
| Gateway | Affected bank is hidden from bank selection |
| In-progress payments | AcceptedSettlementInProcess payments complete after maintenance ends |
AcceptedSettlementInProcessAcceptedSettlementCompletedBlink Data: a bank that is unavailable, including during maintenance, returns 503 BDT601 on consent creation and data requests. Retry with exponential backoff, and if it persists, try again later.
Include these headers with your API requests to enable better tracing and debugging. Send request-id, x-correlation-id and idempotency-key as UUIDs: Blink Payments rejects any other value with 400.
| Header | Purpose | Example |
|---|---|---|
request-id | Unique identifier for each request. Generate a UUID. Include when contacting support. | request-id: 550e8400-e29b-41d4-a716-446655440000 |
x-correlation-id | Track multiple related requests in a session. Useful for debugging multi-step flows. | x-correlation-id: 9b2f6c1e-4d7a-4e8b-a0c3-2f5d8e1b7a64 |
idempotency-key | Prevent duplicate payments (Blink Payments) or consents (Blink Data, POST /v1/consents only). Generate a UUID per request and reuse it when you retry that request. | idempotency-key: 3f1c9a2e-7b4d-4c6e-9a8f-1d2e3b4c5a6f |
x-customer-ip | Customer's IP address. Helps with fraud detection and debugging. | x-customer-ip: 203.0.113.42 |
Request ID for Support
When contacting BlinkPay support, include the request-id from your API calls. This allows BlinkPay to quickly locate your request in their logs.
For debugging and audit purposes:
request-id returned in response headersIf issues persist after troubleshooting, contact support@blinkpay.co.nz with your environment, consent/payment IDs, and steps to reproduce.
Check system status: www.blinkpay.co.nz/service-status
Sign up for updates on that page to be notified of any incidents.