# BlinkPay Documentation: Blink Data pages and the shared pages
> Every page below is the markdown its .md URL serves, in reading order. The page index is https://merchants.blinkpay.co.nz/docs/llms.txt. The OpenAPI specifications are https://merchants.blinkpay.co.nz/docs/debit/openapi.yaml (Blink Payments) and https://merchants.blinkpay.co.nz/docs/data/openapi.yaml (Blink Data).
# Blink Data Essentials (https://merchants.blinkpay.co.nz/docs/data/essentials.md)
---
title: Blink Data Essentials
description: The facts every Blink Data integration needs, on one page - base URLs, token endpoint, credentials, the consent request, sandbox test values, return parameters, polling, reading and refreshing data, and idempotency - each linked to its detail.
---
# Blink Data Essentials
The facts most Blink Data integrations need, on one page. Each section links to the page with the detail.
## Base URLs and Token Endpoint
| Environment | Base URL | Token endpoint |
| ----------- | ------------------------------------- | ------------------------------------------------------- |
| Sandbox | `https://sandbox.data.blinkpay.co.nz` | `POST https://sandbox.data.blinkpay.co.nz/oauth2/token` |
| Production | `https://data.blinkpay.co.nz` | `POST https://data.blinkpay.co.nz/oauth2/token` |
API paths sit under `/v1`, for example `POST /v1/consents`; the token endpoint sits at the host root. Get a token with the OAuth 2.0 client credentials grant; it lasts one hour, so reuse it until it expires. You don't request scopes: the token carries the ones assigned to your API client. Keep the client secret on your server, never in a browser or mobile app. Blink Data has no SDK; call it over HTTPS, and try requests with the [Postman Collection](/docs/shared/reference/postman). See [Authentication](/docs/shared/authentication).
## Credentials and Going Live
Sandbox credentials are provisioned when you sign up at [merchants.blinkpay.co.nz](https://merchants.blinkpay.co.nz/). On **Settings > API Settings**, open the **Sandbox** row, copy the **API Client ID** and select **Rotate Secret** to get a secret, which is shown once. If there is no sandbox key, select **Request Sandbox API Key** on that page. Register each `redirect_uri` under **Whitelisted Redirect URLs** on the same row; an unregistered one is rejected with 400 `BDT211`.
For production, select **Upgrade to Production**. An Administrator submits the application, and approval usually takes 1-3 business days. See [Getting API Credentials](/docs/shared/authentication#getting-api-credentials) and [Going Live](/docs/shared/help/going-live).
## Creating a Consent
`POST /v1/consents` takes a `flow`, whose `flow.detail.type` is `gateway`, `redirect` or `decoupled`, and the `permissions` you need (`ReadAccounts`, `ReadBalances`, `ReadTransactions`, `ReadStatements`, `ReadParty`). `expiration_date_time` is optional; leave it out for a consent that doesn't expire. `start_date_time` and `end_date_time` set the window of data the consent covers. A gateway `flow_hint` sits beside `flow.detail` and must name a `bank` (400 `BDT260` without one). Blink Data consents don't take a metadata object. See [Quick Start](/docs/data/quick-start) and [Consent Duration](/docs/data/concepts/consent-lifecycle#consent-duration).
## Sandbox Test Values
| Item | Value |
| ------------------------- | ---------------------------------------------------- |
| Test bank | **PNZ**, the only bank in the sandbox |
| Redirect or gateway login | Username `user01`, password `password` |
| Decoupled, auto-approve | Mobile number `+64-259531933` |
| Decline | **Cancel** on the PNZ login page, before logging in |
Any other decoupled number is accepted but never authorises. A bank your organisation isn't set up for returns 400 `BDT213`. See [Testing](/docs/data/testing#sandbox-test-values).
## Return Parameters
When the customer comes back to your `redirect_uri`, the query string carries `cid`, the consent ID. Only when the outcome is not success does it also carry `status` (`cancelled`, `rejected`, `timeout` or `error`; the gateway can also send `pending`) and a customer-safe `error` message. There is no success value: a return without `status` means the outcome was not reported as failed. Always confirm with `GET /v1/consents/{consent_id}`. See [Return Parameters](/docs/shared/flows/redirect-flow#return-parameters).
## Consent Status and Polling
`Authorised` means you can read data. `Rejected`, `Revoked`, `Consumed` (the consent period has ended) and `GatewayTimeout` are final; create a new consent. Don't poll while the customer is at their bank. Once they return, read the consent and, if it isn't final yet, poll it every second. In the decoupled flow, wait for the `callback_url` or poll every 5 seconds. See [Consent Lifecycle](/docs/data/concepts/consent-lifecycle).
## Reading and Refreshing Data
Every data endpoint takes the consent as `?consent_id=`. Lists are paged with `page` and `size` (at most 500); see [Pagination](/docs/data/guides/data-retrieval#pagination). Date filters need a time zone offset: use `Z`, or encode `+` as `%2B`. To read new data later, call the endpoints again with the same `consent_id`; the customer doesn't need to do anything until the consent expires or is revoked. See [Data Retrieval](/docs/data/guides/data-retrieval) and [Refreshing Data](/docs/data/concepts/consent-lifecycle#refreshing-data).
## Idempotency
Send an `idempotency-key` header with a UUID on each `POST /v1/consents`, and send the same key when you retry: you get the original consent back rather than a second one. Keys are remembered for 24 hours. The same key with a different body is rejected with 409 `BDT701`. See [Idempotency](/docs/data/quick-start#idempotency).
## Limits
If you expect high volume, tell support in advance. See [API Policies](/docs/shared/reference/api-policies).
## Errors and Help
Blink Data error codes start with `BDT`; see [Error Codes](/docs/data/error-codes). For common problems, see [Troubleshooting](/docs/shared/help/troubleshooting).
# Getting Started with Blink Data (https://merchants.blinkpay.co.nz/docs/data.md)
---
title: Getting Started with Blink Data
description: Blink Data gives your application customer-authorised access to account, balance, transaction and statement data from New Zealand banks.
layout: home
hero:
name: Blink Data
text: Account Information APIs
tagline: Secure access to customer-authorised financial data from NZ banks
actions:
- theme: brand
text: Get Started
link: /docs/data/quick-start
- theme: alt
text: Data Retrieval Guide
link: /docs/data/guides/data-retrieval
- theme: alt
text: Payment APIs
link: /docs/debit
features:
- icon: π¦
title: Multi-Bank Support
details: Connect with ANZ, ASB, BNZ, Westpac, Kiwibank and NZHL through a single integration. Access real-time account data securely.
- icon: π
title: Customer-Authorised
details: All data access requires explicit customer consent with defined permissions and, optionally, an expiry date.
- icon: π
title: Comprehensive Data
details: Access accounts, balances, transactions, statements, and party information through standardised APIs.
- icon: β‘
title: Real-Time Access
details: Get near real-time account data directly from banks. No screen scraping or outdated data.
- icon: π
title: Ongoing Consents
details: One consent gives ongoing data access until it expires or the customer revokes it.
- icon: π οΈ
title: Easy Integration
details: Gateway Flow handles bank selection and authentication. Single API for all banks.
---
Using an AI coding assistant? Connect it to the [BlinkPay MCP server](/docs/shared/reference/ai-tools), which has all of this documentation and the API schemas.
## Quick Start
Get started with Blink Data in minutes:
```bash
# Authenticate
curl -X POST https://sandbox.data.blinkpay.co.nz/oauth2/token \
-d "client_id=YOUR_ID&client_secret=YOUR_SECRET&grant_type=client_credentials"
# Create consent
curl -X POST https://sandbox.data.blinkpay.co.nz/v1/consents \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"flow": {
"detail": {
"type": "gateway",
"redirect_uri": "https://yourapp.com/callback"
}
},
"permissions": ["ReadAccounts", "ReadBalances", "ReadTransactions"],
"expiration_date_time": "2026-12-31T23:59:59+13:00"
}'
```
## Environment URLs
The Blink Data base URL for each environment, with its token endpoint. API paths sit under `/v1`; the token endpoint sits at the host root.
| Environment | Base URL | Token endpoint |
| ----------- | -------------------------------------- | ------------------------------------------------------- |
| Sandbox | `https://sandbox.data.blinkpay.co.nz` | `POST https://sandbox.data.blinkpay.co.nz/oauth2/token` |
| Production | `https://data.blinkpay.co.nz` | `POST https://data.blinkpay.co.nz/oauth2/token` |
In the sandbox, use the test bank `PNZ`; the production banks (ANZ, ASB, BNZ, Westpac, Kiwibank and NZHL) are used in production. See [Sandbox and Production Banks](/docs/data/testing#sandbox-and-production-banks).
## Choose Your Integration Path
### Gateway Flow (Strongly Recommended)
Let BlinkPay handle the bank selection and authentication UI. Customer selects their bank and authentication method through our hosted gateway.
[Learn more about Gateway Flow β](/docs/shared/flows/gateway-flow)
### Redirect Flow
Direct browser-based flow where customers authenticate with their bank and return to your site.
[Learn more about Redirect Flow β](/docs/shared/flows/redirect-flow)
### Decoupled Flow
Customer authorises on a different device or at a different time via their banking app.
[Learn more about Decoupled Flow β](/docs/shared/flows/decoupled-flow)
## Data Access APIs
All Data APIs are documented in the comprehensive Data Retrieval guide:
### Available APIs
- **Accounts API** - Get list of customer accounts including account numbers, types, names, and currencies
- **Balances API** - Access current and available balances for customer accounts with timestamps
- **Transactions API** - Retrieve transaction history with detailed payee information and filtering options
- **Statements API** - Download account statements (availability varies by bank, most support at least 2 years)
- **Party API** - Get account holder information including name and contact details
[Explore Data Retrieval Guide β](/docs/data/guides/data-retrieval)
## Use Cases
### Personal Finance Apps
Aggregate accounts from multiple banks into a single view.
### Lending & Credit Assessment
Verify income and assess creditworthiness using actual bank data.
### Accounting Software
Automate bank reconciliation and transaction categorisation.
### Budgeting Tools
Track spending patterns and provide insights.
### Wealth Management
Provide holistic financial overview across institutions.
## What You'll Need
**Prerequisites**
- A BlinkPay account: sign up at [merchants.blinkpay.co.nz](https://merchants.blinkpay.co.nz/); sandbox credentials are provisioned at sign-up (see [Getting API Credentials](/docs/shared/authentication#getting-api-credentials))
- OAuth 2.0 client credentials (Client ID and Secret)
- HTTPS-enabled redirect URI for production
## Integration Standards
Before you go live, read [Credential and Integration Security](/docs/shared/standards/credential-security) for how to store credentials and handle outcomes safely, and, if you use the redirect or decoupled flow directly rather than the Gateway, [Consent Experience for Direct Flows](/docs/shared/standards/consent-experience) for what your consent screens must show.
## Support
[View all support resources β](/docs/shared/help/support)
## Next Steps
1. [Understand the basics](/docs/data/overview) - Learn about Blink Data architecture
2. [Review bank coverage](/docs/shared/bank-coverage) - See which features each bank supports
3. [Follow the quick start guide](/docs/data/quick-start) - Create your first consent in minutes
4. [Explore authentication flows](/docs/shared/flows/gateway-flow) - Choose the right flow for your use case
# Blink Data Overview (https://merchants.blinkpay.co.nz/docs/data/overview.md)
---
title: Blink Data Overview
description: Secure account information access through open banking
---
# Blink Data Overview
Blink Data is BlinkPay's Account Information API that provides secure access to customer-authorised financial data from major New Zealand banks through Open Banking.
## What is Blink Data?
Blink Data allows businesses to access account information directly from customer bank accounts using the newest data access methods provided by participating banks. It integrates with data recipients and banks, enabling customers to use a modern consent process to share their financial data securely.
Customers can grant access to their account information, avoid traditional document-based verification processes, and obtain greater control over when, where, and how their data is shared.
### Key Features
- **Multi-Bank Aggregation** - Single API for ANZ, ASB, BNZ, Westpac, Kiwibank and NZHL
- **Real-Time Data** - Near real-time access to account information
- **No Screen Scraping** - Official bank APIs only
- **Customer-Authorised** - Explicit consent required
- **Secure** - ISO 27001:2022 certified, encrypted data transmission
## Architecture
Blink Data operates as an aggregation layer between data recipients and banks:
```
Your Application ββ Blink Data API ββ Bank APIs ββ Customer Accounts
```
### Benefits of Aggregation
- **Single Integration** - Integrate once with BlinkPay instead of separately with each bank
- **Stable API** - Your code remains unchanged as banks upgrade their APIs
- **Consistent Experience** - Unified API across all banks
- **Reduced Maintenance** - BlinkPay handles bank-specific changes
## Supported Banks
In production, Blink Data connects to ANZ, ASB, BNZ, Westpac, Kiwibank and NZHL. The sandbox has one test bank, PNZ. Every bank supports the redirect and decoupled flows and returns accounts, balances, transactions and party data; statements are available at some banks only. The per-bank table, including the `bank` values and decoupled identifiers, is on the [Bank Coverage](/docs/shared/bank-coverage) page. Blink Data has no payment limits; the limits on that page apply to Blink Payments.
## Available Data
| Permission | Data Access |
| ------------------ | ----------------------------------------------------------------------------- |
| `ReadAccounts` | Account numbers, names, types, currency |
| `ReadBalances` | Current and available balances |
| `ReadTransactions` | Transaction history with payee info |
| `ReadStatements` | Account statements (availability varies, most banks support at least 2 years) |
| `ReadParty` | Account holder name and contact details |
**Bank Support Varies**
Not all banks support all data types. Use the metadata endpoint (`GET /v1/meta`) to check feature availability.
## How It Works
1. **Customer Initiates** - Customer wants to connect their bank account
2. **Create Consent** - Your application creates a data access consent
3. **Customer Authorises** - Customer authenticates with their bank and approves access
4. **Access Data** - Your application retrieves authorised account information
5. **Ongoing Access** - Continue accessing data until consent expires or is revoked
## Data Handling
Blink Data operates as an integration layer and does not hold customer Personally Identifiable Information for first-party use:
- **Customer-Authorised** - All data access requires explicit customer consent
- **Time-Limited** - Consents can carry an expiry date, and start and end dates that limit the data they cover
- **Customer Control** - Customers can revoke consent anytime through the same channel used to create it
## Environment URLs
API paths sit under `/v1`; the token endpoint sits at the host root.
| Environment | Base URL | Token endpoint |
| ----------- | ------------------------------------- | ------------------------------------------------------- |
| Sandbox | `https://sandbox.data.blinkpay.co.nz` | `POST https://sandbox.data.blinkpay.co.nz/oauth2/token` |
| Production | `https://data.blinkpay.co.nz` | `POST https://data.blinkpay.co.nz/oauth2/token` |
## Getting Started
1. **Get Credentials** - Sign up at [merchants.blinkpay.co.nz](https://merchants.blinkpay.co.nz/); sandbox credentials are under **Settings > API Settings** (see [Getting API Credentials](/docs/shared/authentication#getting-api-credentials))
2. **Import Postman** - Download from [Postman workspace](https://www.postman.com/blinkpay/blinkpay-organisation-workspace/overview)
3. **Generate Token** - Use client credentials OAuth flow
4. **Test** - Try `GET /v1/meta` to view available banks
5. **Integrate** - Implement consent flows and data retrieval
6. **Go Live** - Select **Upgrade to Production** in the portal and, once approved, switch to production credentials (see [Going Live](/docs/shared/help/going-live))
## Next Steps
- **[Quick Start Guide](/docs/data/quick-start)** - Get started in minutes
- **[Data Retrieval](/docs/data/guides/data-retrieval)** - Complete API reference
# Quick Start (https://merchants.blinkpay.co.nz/docs/data/quick-start.md)
---
title: Quick Start
description: Get started with Blink Data API
---
# Quick Start
This guide demonstrates how to access customer account data using the Blink Data API.
## Requirements
- BlinkPay client account with Data API access
- API credentials (client_id and client_secret) from **Settings > API Settings** in the portal; see [Getting API Credentials](/docs/shared/authentication#getting-api-credentials)
- A redirect URI registered under **Whitelisted Redirect URLs** on the same page; see [Redirect URI Whitelisting](/docs/shared/authentication#redirect-uri-whitelisting)
## Environment URLs
| Environment | Base URL |
| -------------- | ------------------------------------- |
| **Sandbox** | `https://sandbox.data.blinkpay.co.nz` |
| **Production** | `https://data.blinkpay.co.nz` |
## Step 1: Get Access Token
**Request:**
```http
POST /oauth2/token HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}
```
**Response:**
```json
{
"access_token": "eyJhbGc...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "manage:consents read:meta read:accounts read:balances read:transactions read:party"
}
```
Tokens last about 1 hour (`expires_in`). Cache the token and reuse it for every call until it expires; request a new one only when it has expired or the API returns 401, then retry the call. Don't request a new token per request. You don't request scopes: the token carries the scopes assigned to your API client.
## Step 2: Create Account Access Consent
**Request:**
```http
POST /v1/consents HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
idempotency-key: 6f1c2b8e-4a7d-4c1e-9b3f-2d5e8a9c0f14
{
"flow": {
"detail": {
"type": "redirect",
"bank": "PNZ",
"redirect_uri": "https://your-site/callback"
}
},
"permissions": [
"ReadAccounts",
"ReadBalances",
"ReadTransactions"
],
"start_date_time": "2025-10-01T00:00:00+13:00",
"end_date_time": "2026-12-31T23:59:59+13:00",
"expiration_date_time": "2026-12-31T23:59:59+13:00"
}
```
`start_date_time` and `end_date_time` set the window of data the consent covers: you can retrieve transactions and statements dated between them. `start_date_time` can't be in the future, `end_date_time` must be after today (New Zealand date), and the two must fall on different dates. Leave `end_date_time` out to read up to the latest available transaction, and change the example dates once they have passed. `expiration_date_time` is when access ends; it can't be on the day you create the consent or before `end_date_time`, and you can omit it for a consent that doesn't expire. See [Consent Duration](/docs/data/concepts/consent-lifecycle#consent-duration).
**Response:**
```json
{
"consent_id": "c8327a00-3f1a-4d10-a27a-aece83eadabc",
"redirect_uri": "{bank_authorisation_url}"
}
```
For the redirect flow, `redirect_uri` is the bank's authorisation URL. (A gateway consent returns the BlinkPay gateway URL instead, and a decoupled consent returns only `consent_id`.)
### Idempotency
Send an `idempotency-key` header with a UUID on each `POST /v1/consents`, and reuse the same key if you retry that request. The retry returns the original consent instead of creating a second one.
## Step 3: Redirect Customer
Redirect the customer to the `redirect_uri` to authorise data access at their bank. The customer chooses which accounts to share there, so a consent can cover more than one account.
**Test Bank (PNZ):**
- Username: `user01`
- Password: `password`
## Step 4: Handle Callback
The customer returns to your redirect URI with the consent ID in `cid`:
```
https://your-site/callback?cid=c8327a00-3f1a-4d10-a27a-aece83eadabc
```
If authorisation didn't succeed, BlinkPay adds `status` (`cancelled`, `rejected`, `timeout` or `error`; the Gateway Flow can also send `pending`) and `error`, a message you can show the customer:
```
https://your-site/callback?cid=c8327a00-3f1a-4d10-a27a-aece83eadabc&status=rejected&error=The+consent+has+been+rejected
```
There is no success value. Branch on `status`, never on `error`, and always confirm the outcome by checking the consent status (Step 5), whether or not `status` is present. The full table is in [Return Parameters](/docs/shared/flows/redirect-flow#return-parameters).
## Step 5: Check the Consent Status
**Request:**
```http
GET /v1/consents/{consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Response:**
```json
{
"consent_id": "c8327a00-3f1a-4d10-a27a-aece83eadabc",
"status": "Authorised",
"status_update_date_time": "2026-10-07T10:15:30+13:00",
"creation_date_time": "2026-10-07T10:14:02+13:00",
"flow": {
"detail": {
"type": "redirect",
"bank": "PNZ",
"redirect_uri": "https://your-site/callback"
}
},
"permissions": ["ReadAccounts", "ReadBalances", "ReadTransactions"],
"start_date_time": "2025-10-01T00:00:00+13:00",
"end_date_time": "2026-12-31T23:59:59+13:00",
"expiration_date_time": "2026-12-31T23:59:59+13:00",
"approved_requests": []
}
```
`approved_requests` lists the data requests made under the consent so far, each with a `type` (`Account`, `Balance`, `Transaction`, `Statement` or `Party`), an `id` and a `creation_date_time`. The response also carries `hashed_customer_identifier` where the consent has one.
`Authorised` means you can retrieve data. If the status is `AwaitingAuthorisation` (or `GatewayAwaitingSubmission` in the Gateway Flow), the customer hasn't finished yet: check again every second until it changes. Every other status is final; create a new consent if the customer wants to try again. See [Consent Status](/docs/data/concepts/consent-lifecycle#consent-status) for the full list.
## Step 6: Retrieve Accounts
**Request:**
```http
GET /v1/accounts?consent_id={consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Response:**
```json
{
"accounts": [
{
"account_id": "eyJhY2NvdW50SWQiOiIxMjM0NTY3ODkwIn0",
"name": "Everyday Account",
"type": "current",
"currency": "NZD",
"details": {
"number": "12-3456-7890123-00",
"name": "Everyday Transactional Account"
}
}
],
"pagination": {
"page": 1,
"size": 500,
"total_pages": 1,
"total_records": 1
}
}
```
## Step 7: Get Balances
**Request:**
```http
GET /v1/accounts/{account_id}/balances?consent_id={consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Response:**
```json
{
"balances": [
{
"account_id": "eyJhY2NvdW50SWQiOiIxMjM0NTY3ODkwIn0",
"type": "current",
"amount": {
"amount": "1234.56000",
"currency": "NZD"
},
"direction": "in",
"date_time": "2026-10-07T10:00:00+13:00"
},
{
"account_id": "eyJhY2NvdW50SWQiOiIxMjM0NTY3ODkwIn0",
"type": "available",
"amount": {
"amount": "1734.56000",
"currency": "NZD"
},
"direction": "in",
"date_time": "2026-10-07T10:00:00+13:00"
}
],
"pagination": {
"page": 1,
"size": 500,
"total_pages": 1,
"total_records": 2
}
}
```
## Step 8: Get Transactions
**Request:**
```http
GET /v1/accounts/{account_id}/transactions?consent_id={consent_id}&from_booking_date_time=2026-09-01T00:00:00Z HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
Date filters take a full ISO 8601 date-time with an offset (`2026-09-01T00:00:00Z` or `2026-09-01T00:00:00+12:00`); a date on its own is rejected. The range must sit inside the consent's `start_date_time` to `end_date_time` window. Timestamps in responses are in New Zealand time (`+12:00` or `+13:00`).
**Response:**
```json
{
"transactions": [
{
"transaction_id": "txn-123",
"type": "POS",
"direction": "out",
"amount": {
"amount": "45.50000",
"currency": "NZD"
},
"status": "posted",
"date_time": "2026-09-15T14:23:00+12:00",
"description": "Coffee Shop",
"balance": {
"account_id": "eyJhY2NvdW50SWQiOiIxMjM0NTY3ODkwIn0",
"type": "current",
"amount": {
"amount": "1189.06000",
"currency": "NZD"
},
"direction": "in",
"date_time": "2026-09-15T14:23:00+12:00"
}
}
],
"pagination": {
"page": 1,
"size": 500,
"total_pages": 1,
"total_records": 1
}
}
```
`amount.amount` is never signed: `direction` tells you whether money went `in` or `out`. `transaction_id` is missing at some banks, and `type` is the bank's own transaction code, so its values differ by bank. See [Reading Transactions](/docs/data/guides/data-retrieval#reading-transactions) before you store or categorise transactions.
To fetch new data later, call these endpoints again with the same `consent_id`; the customer doesn't need to authorise again until the consent expires or is revoked. See [Refreshing Data](/docs/data/concepts/consent-lifecycle#refreshing-data).
## Sandbox Testing
**Test Bank:** PNZ (Payments NZ Mock Bank)
| Flow | Test Credentials |
| ------------- | ---------------------------------------- |
| **Redirect** | Username: `user01`, Password: `password` |
| **Decoupled** | Phone: `+64-259531933` (auto-approves) |
Any other mobile number is accepted but never authorises: the consent stays `AwaitingAuthorisation` until the bank's window ends, then becomes `Rejected`. See [Sandbox Test Values](/docs/data/testing#sandbox-test-values) for how to produce a decline and test revocation.
## Next Steps
- **[Data Retrieval Guide](/docs/data/guides/data-retrieval)** - Complete API reference with pagination and error handling
# Authentication (https://merchants.blinkpay.co.nz/docs/shared/authentication.md)
---
title: Authentication
description: Learn how to authenticate with the BlinkPay API using OAuth 2.0
---
# Authentication
BlinkPay uses OAuth 2.0 client credentials flow for API authentication. This guide explains how to obtain and manage access tokens for secure API communication.
## Overview
All BlinkPay API requests require authentication using OAuth 2.0 Bearer tokens. Here's the authentication flow:

## Getting API Credentials
Before you can authenticate, you need a client ID and client secret. Both environments are managed in the one BlinkPay portal at [merchants.blinkpay.co.nz](https://merchants.blinkpay.co.nz/), under **Settings > API Settings**, which lists a row per environment (Sandbox, Production) with its API Client ID. Users with the Administrator or Developer role can open this page; the User role cannot.
The portal never displays an existing client secret. To obtain one, open the environment's row and select **Rotate Secret**: a new secret is generated and shown once, in the confirmation dialog, so copy it into your secret store straight away. Rotating replaces the previous secret immediately (see [Credential Rotation](#credential-rotation)).
### Sandbox Credentials
1. Sign up at [merchants.blinkpay.co.nz](https://merchants.blinkpay.co.nz/). Sandbox API credentials are provisioned when you sign up.
2. Log in and open **Settings > API Settings**, then open the **Sandbox** row.
3. Copy the **API Client ID**, then select **Rotate Secret** to obtain the client secret.
If there is no sandbox key on **Settings > API Settings**, select **Request Sandbox API Key** on that page to ask BlinkPay for one.
### Production Credentials
1. Complete and test your integration in sandbox.
2. Select **Upgrade to Production**, in the top navigation bar or on **Settings > API Settings**. Anyone in your organisation can see the button, but an Administrator must submit the application (business details, services, banking, billing, a security questionnaire and the Client Service Agreement).
3. BlinkPay reviews the application. Production upgrade approval usually takes 1-3 business days.
4. Once approved, open **Settings > API Settings**, open the **Production** row, copy the API Client ID and select **Rotate Secret** to obtain the production secret.
Production has its own list of whitelisted redirect URLs, so register your production redirect URIs separately (see below).
**Keep Credentials Secure**
Never commit credentials to source control or expose them in client-side code. Store them securely using environment variables or secret management systems.
## Redirect URI Whitelisting
The `redirect_uri` you send with a Gateway or Redirect flow consent must match a URL whitelisted for your account, otherwise the request fails with 400 (`BP214` for Blink Payments, `BDT211` for Blink Data). The Decoupled flow's `callback_url` is not checked against this list. Users with the Developer or Administrator role register URLs in the portal under **Settings > API Settings**: open the environment's row and use **Add URL +** under **Whitelisted Redirect URLs**. Sandbox and production have separate lists. If you don't have access, email [support@blinkpay.co.nz](mailto:support@blinkpay.co.nz) with the URI(s) you need registered.
Two formats are supported:
| Format | Example | Matches |
|--------------------|------------------------------------|-------------------------------------------------------------------------------------------------|
| Single host | `https://app.example.com/callback` | `https://app.example.com/callback` and any path that starts with `/callback`, such as `/callback/success` |
| Wildcard subdomain | `https://*.example.com/callback` | Any subdomain: `https://staging.example.com/callback`, `https://a.b.example.com/callback`, etc. |
The scheme, host and port must match exactly; the host comparison is case-insensitive. The path is a prefix match on the raw string, so registering `/callback` accepts `/callback/success` but also `/callbackfoo`; register a path that ends in a distinct segment. A registered trailing slash makes the slash-less form fail: registering `/callback/` rejects `/callback`, so register `/callback`. Query strings are ignored on both sides, so you can append your own (for example `?order=123`).
Wildcard entries match subdomains only β the bare domain (`example.com`) is not matched by `*.example.com` and must be registered separately. The wildcard `*.` must appear at the very start of the hostname. Only a single `*.` prefix is allowed, and the domain after `*.` must have at least two labels (e.g., `*.example.com` is valid, `*.com` is not).
Custom app schemes such as `myapp://callback` are accepted in both environments, provided they have a host part (`myapp:callback` is rejected). Production rejects `http://` URLs and `localhost`; sandbox allows them for local development.
## Obtaining an Access Token
### Token Endpoint
Request a token from the host of the API you are calling:
| API | Sandbox | Production |
|----------------|-----------------------------------------------------|---------------------------------------------|
| Blink Payments | `POST https://sandbox.debit.blinkpay.co.nz/oauth2/token` | `POST https://debit.blinkpay.co.nz/oauth2/token` |
| Blink Data | `POST https://sandbox.data.blinkpay.co.nz/oauth2/token` | `POST https://data.blinkpay.co.nz/oauth2/token` |
### Request Format
The `client_credentials` grant type is the only supported grant type.
```http
POST /oauth2/token HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET
```
For Blink Data, send the same request with `Host: sandbox.data.blinkpay.co.nz` (or `data.blinkpay.co.nz` in production), and use the token it returns for Blink Data calls. Both products also accept `client_id` and `client_secret` as HTTP Basic credentials instead of form fields.
**Blink Payments only:** for language-specific examples, see the [SDK documentation on GitHub](https://github.com/BlinkPay).
### Success Response
A Blink Payments token response:
```json
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "view:metadata view:single_consent create:single_consent view:enduring_consent create:enduring_consent view:payment create:payment create:quick_payment"
}
```
A Blink Data token response has the same shape, with Blink Data scopes, for example `"scope": "manage:consents read:meta read:accounts read:balances read:transactions read:party"`.
**Response Fields:**
- `access_token` - The Bearer token to use for API requests
- `token_type` - Always "Bearer"
- `expires_in` - Token lifetime in seconds (3600 = 1 hour)
- `scope` - The scopes assigned to your API client (space-separated list). You don't request scopes; see [Scope Assignment](#scope-assignment)
### Error Responses
**Blink Payments:** token request failures use the standard BlinkPay error body. A wrong client ID or secret returns 401:
```json
{
"timestamp": "2026-10-07T10:15:30.123+13:00",
"path": "/oauth2/token",
"status": 401,
"error": "UNAUTHORIZED",
"message": "Invalid client credentials"
}
```
Wrong or missing credentials return 401 (an unsupported grant type, 403). Check the client ID, secret and environment; branch on the HTTP status, not the `message` text.
## Using Access Tokens
Include the access token in the `Authorization` header with the `Bearer` prefix. A Blink Payments call:
```http
GET /payments/v1/payments/{payment_id} HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
A Blink Data call:
```http
GET /v1/accounts?consent_id={consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
## Token Management
### Token Expiration
Access tokens expire after **1 hour** (3600 seconds). Your application should:
1. Cache the token with its expiry time
2. Request a new token before expiry (e.g., 5 minutes before)
3. Handle concurrent requests with a single token refresh
### Handling 401 Errors
On both products, when an API call is refused because of the access token, the 401 response has an empty body and a `WWW-Authenticate: Bearer error="invalid_token"` header (a bare `Bearer` if no token was sent). The response is the same whether the token has expired, is malformed or was not issued for this environment, so don't try to parse a body. When you receive a 401 Unauthorised response:
1. **Request a new token** - Your token may have expired
2. **Retry the request** - Use the new token
3. **Check credentials** - If still failing, verify client ID and secret, and that they belong to the environment you are calling
### Request IDs
**Blink Payments:** API responses carry a `request-id` header. If you send your own `request-id` (a UUID) it is echoed back; otherwise BlinkPay generates one. Log it with each call and include it when you contact support about a specific request.
### Token Caching
**Best Practice**
Cache access tokens until they expire rather than requesting a new token for every API call. This reduces latency and load on the auth server.
**Recommended Approach:**
- Cache token with expiry time
- Refresh 5 minutes before expiry
- Handle concurrent requests with a single token refresh
- Store token in memory (not in database for each request)
## OAuth 2.0 Scopes
BlinkPay uses scopes to control API access. Each product has its own set.
### Payment API Scopes
**Blink Payments only.**
| Scope | Description |
|---------------------------|----------------------------------------------------|
| `view:metadata` | View API metadata |
| `view:single_consent` | Retrieve single consent details |
| `create:single_consent` | Create single consents |
| `view:enduring_consent` | Retrieve enduring consent details |
| `create:enduring_consent` | Create enduring consents |
| `revoke:single_consent` | Revoke a single consent that has not been Consumed |
| `revoke:enduring_consent` | Revoke an enduring payment consent |
| `view:payment` | Retrieve payment details |
| `create:payment` | Initiate payments |
| `view:transaction` | View transaction history |
| `create:quick_payment` | Create quick payments |
| `view:quick_payment` | View quick payments |
| `create:refund` | Create refunds |
| `view:refund` | Retrieve refund details |
| `create:subscription` | Create webhook subscriptions |
| `view:subscription` | View webhook subscriptions |
| `delete:subscription` | Delete webhook subscriptions |
Revoking a quick payment uses `revoke:single_consent`, and Fixed Recurring Payments use the enduring consent scopes: `create:enduring_consent` to create one, `view:enduring_consent` to retrieve it and `revoke:enduring_consent` to cancel it.
### Data API Scopes
**Blink Data only.**
| Scope | Endpoints |
|---------------------|-----------------------------------------------------------------------------------------------|
| `manage:consents` | Create, retrieve and revoke consents (`POST`, `GET` and `DELETE` on `/v1/consents`) |
| `read:meta` | `GET /v1/meta` |
| `read:accounts` | `GET /v1/accounts` and `GET /v1/accounts/{account_id}` |
| `read:balances` | `GET /v1/accounts/{account_id}/balances` |
| `read:transactions` | `GET /v1/accounts/{account_id}/transactions` and all statement endpoints (list, single statement and statement file) |
| `read:party` | `GET /v1/party` and `GET /v1/accounts/{account_id}/party` |
The consent must also carry the matching permission (for example `ReadStatements`) for each data call.
### Scope Assignment
You don't request scopes. BlinkPay assigns them to your API client, and every token carries all of them in its `scope` field. Contact support if you need a scope you don't have.
**Scope Restrictions**
If you attempt to use an API endpoint without the required scope, you'll receive a 403 Forbidden error.
## Security Best Practices
For the full set of expectations on handling credentials and securing your integration, see [Credential and Integration Security](/docs/shared/standards/credential-security).
### Credential Storage
Store credentials securely using environment variables or secret management systems (AWS Secrets Manager, HashiCorp Vault). Encrypt credentials at rest and restrict access to necessary personnel only. Never commit credentials to source control, store them in code files, expose them in client-side code, or share them via email or chat.
### Token Security
Transmit tokens over HTTPS only and store them in memory rather than localStorage in browsers. Implement token rotation if a compromise is suspected, and log authentication failures for monitoring. Never log full access tokens, send tokens in URL parameters, store tokens in cookies without secure flags, or share tokens between services unnecessarily.
### HTTPS Only
All API calls must use HTTPS. HTTP requests will be rejected.
### Credential Rotation
Rotate credentials every 90 days, immediately if compromised, or when staff with access leave. Use the Client Portal to rotate your client secret (**Settings > API Settings**, open the environment's row, **Rotate Secret**).
There is no grace period. The old secret stops working for new token requests the moment you rotate. Access tokens already issued are not stopped: they keep working until they expire, which by default is one hour after they were issued. Have the new secret ready to deploy as soon as it is shown, and expect token requests from anything still holding the old secret to fail with 401 until it is updated.
## Using Client SDKs
**Blink Payments:** the official Blink Payments SDKs (Java, Node.js, .NET and PHP, on [GitHub](https://github.com/BlinkPay)) handle authentication automatically. Provide your credentials and the SDK manages the token lifecycle for you.
**Blink Data:** there is no SDK. Call the API over HTTPS with any HTTP client, requesting and caching tokens as described above. The [Postman Collection](/docs/shared/reference/postman) has ready-made Blink Data requests to start from.
## Testing Authentication
Use sandbox credentials to test authentication without affecting production. The [Postman Collection](/docs/shared/reference/postman) includes a pre-configured "Generate access token" request.
## Troubleshooting
For common authentication errors (invalid credentials, expired tokens, grant type issues) and their solutions:
[View Troubleshooting Guide β](/docs/shared/help/troubleshooting#authentication-issues)
## Next Steps
- **[Gateway Flow](/docs/shared/flows/gateway-flow)** - Start your integration with BlinkPay's hosted bank selection page
# Permissions (https://merchants.blinkpay.co.nz/docs/data/concepts/permissions.md)
---
title: Permissions
description: Understanding data access permissions
---
# Permissions
Permissions define what data can be accessed through an authorised consent. Request only the permissions you need - customers are more likely to approve minimal, relevant permissions.
## Available Permissions
| Permission | Data Access |
| ------------------ | --------------------------------------- |
| `ReadAccounts` | Account numbers, names, types, currency |
| `ReadBalances` | Current and available balances |
| `ReadTransactions` | Transaction history with payee info |
| `ReadStatements` | Account statements |
| `ReadParty` | Account holder name and contact details |
## Permissions and Endpoints
Each data endpoint needs the matching permission on the consent, which the customer approved when they authorised it:
| Permission | Endpoints |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ReadAccounts` | `GET /v1/accounts`, `GET /v1/accounts/{account_id}` |
| `ReadBalances` | `GET /v1/accounts/{account_id}/balances` |
| `ReadTransactions` | `GET /v1/accounts/{account_id}/transactions` |
| `ReadStatements` | `GET /v1/accounts/{account_id}/statements`, `GET /v1/accounts/{account_id}/statements/{statement_id}`, `GET /v1/accounts/{account_id}/statements/{statement_id}/file` |
| `ReadParty` | `GET /v1/party`, `GET /v1/accounts/{account_id}/party` |
If the consent doesn't include the permission an endpoint needs, the call returns 403; create a new consent that includes it.
## Requesting Permissions
Include the permissions array when creating a consent:
```json
{
"permissions": ["ReadAccounts", "ReadBalances", "ReadTransactions"]
}
```
**Bank Support Varies**
Not all banks support all permissions: statements in particular are available at some banks only (see [Bank Coverage](/docs/shared/bank-coverage)). A redirect or decoupled consent that requests a permission the bank doesn't offer is rejected with 400; in the Gateway Flow the check happens when the customer chooses their bank. Use the metadata endpoint (`GET /v1/meta`) to check feature availability before requesting permissions.
## Permission Details
### ReadAccounts
Returns account identifiers and basic details:
- Account ID (used for subsequent API calls)
- Account number
- Account name and type
- Currency
### ReadBalances
Returns balance information:
- Current balance
- Available balance
- Balance timestamp
### ReadTransactions
Returns transaction history:
- Transaction ID and type
- Amount and direction (in/out)
- Date and description
- Running balance
Use `from_booking_date_time` and `to_booking_date_time` parameters to filter by date range.
### ReadStatements
Returns account statements, a single statement, and the statement file. Available at some banks only (see [Bank Coverage](/docs/shared/bank-coverage), or `features.statements` in `GET /v1/meta`).
### ReadParty
Returns account holder information:
- Name
- Contact details (where available)
# Consent Lifecycle (https://merchants.blinkpay.co.nz/docs/data/concepts/consent-lifecycle.md)
---
title: Consent Lifecycle
description: Understanding data consent states and management
---
# Consent Lifecycle
Data consents authorise ongoing access to customer account information. This page covers consent status, duration, and management.
## Consent Status
| Status | Description | Terminal? |
| --------------------------- | ----------------------------------------------------------------------------------------- | ------------- |
| `GatewayAwaitingSubmission` | Waiting for customer to begin approval | No |
| `AwaitingAuthorisation` | Waiting for customer approval at bank | No |
| `Authorised` | Customer approved - data can be accessed | No |
| `Consumed` | The consented action is complete: the consent period ended at its `expiration_date_time`. Retrieving data never consumes a Data consent | Yes |
| `GatewayTimeout` | Gateway session expired (after 15 minutes) before a bank was chosen | Yes (Failure) |
| `Rejected` | Declined by the customer or bank, or the authorisation window elapsed | Yes (Failure) |
| `Revoked` | Consent revoked by you (`DELETE`) or by the customer at their bank | Yes (Failure) |
### Status Changes
| From State | To State | Cause |
| --------------------------- | ----------------------- | -------------------------------------------------------------- |
| `GatewayAwaitingSubmission` | `AwaitingAuthorisation` | Customer chose their bank at the Gateway |
| `GatewayAwaitingSubmission` | `Rejected` | Customer declined at the Gateway |
| `GatewayAwaitingSubmission` | `GatewayTimeout` | Gateway session cutoff elapsed before bank selection |
| `AwaitingAuthorisation` | `Authorised` | Customer approved |
| `AwaitingAuthorisation` | `Rejected` | Customer or bank declined, or the authorisation window elapsed |
| `Authorised` | `Consumed` | `expiration_date_time` reached |
| `GatewayAwaitingSubmission` | `Revoked` | Revoked by you (`DELETE`) before the customer chose a bank |
| `AwaitingAuthorisation` | `Revoked` | Revoked by you (`DELETE`) before the customer finished |
| `Authorised` | `Revoked` | Revoked by you (`DELETE`) or by the customer at their bank |
Only Gateway Flow consents reach `GatewayAwaitingSubmission`.
Terminal statuses (`Consumed`, `GatewayTimeout`, `Rejected`, `Revoked`) are permanent. A terminal consent can't be used to access accounts or retrieve data, can't be reused and can't be recovered - create a new consent.
## Checking Status
```http
GET /v1/consents/{consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Polling guidance:**
- **Gateway/Redirect flows:** Don't poll while the customer is at their bank. Check status once they return to your redirect URI, every second until it is terminal
- **Decoupled flow:** Poll every 5 seconds until terminal status, or wait for the `callback_url` and check then
The response carries `status`, `status_update_date_time`, `creation_date_time`, `flow`, `permissions`, the three date-time fields below, `approved_requests` and `hashed_customer_identifier`; see [Check the Consent Status](/docs/data/quick-start#step-5-check-the-consent-status) for an example.
## Consent Duration
Data consents use three date-time fields:
| Field | Description |
| ---------------------- | ------------------------------------------------------------------------------- |
| `start_date_time` | Start of the window of data the consent covers: the earliest transactions and statements you can retrieve |
| `end_date_time` | End of the window of data the consent covers: the latest transactions and statements you can retrieve |
| `expiration_date_time` | When access ends and the consent becomes `Consumed`. Optional; omit it for a consent that doesn't expire |
`start_date_time` and `end_date_time` limit which data you can read; `expiration_date_time` limits how long you can read it. Your transaction and statement date filters must sit inside the start-to-end window. How far back you can read transactions is set by `start_date_time`; if you leave it out, the customer's bank decides how much history it returns.
All date-time fields must be ISO 8601 format with timezone offset: `2026-03-20T15:30:00+13:00`. When you create a consent, `start_date_time` can't be in the future, `end_date_time` must be after today (New Zealand date; leave it out to read up to the latest available transaction), and the two must fall on different dates (New Zealand time). `expiration_date_time` can't fall on the day the consent is created, or before `start_date_time` or `end_date_time`. A consent that breaks one of these rules is rejected with 400; the codes are on the [Error Codes](/docs/data/error-codes#_400-bad-request) page.
**Duration Notes**
- Consents remain active until expiration unless revoked
- BlinkPay sets no maximum duration, but banks may apply their own limits. For example, BNZ accepts a `start_date_time` no more than 2 years back
- All times are stored and returned in NZST/NZDT
## Timeout Behaviour
| Flow | Timeout |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| Gateway Flow | 15 minutes (by default) while the customer is still choosing a bank. Once they choose one, that bank's redirect or decoupled window applies |
| Redirect Flow | Set per bank, up to 10 minutes |
| Decoupled Flow | Set per bank, from about 4 minutes |
The redirect and decoupled windows are configured per bank and change as banks change their own limits, so treat them as per-bank values rather than hard-coding a timeout β see [Timeouts and Deadlines](/docs/shared/bank-coverage#timeouts-and-deadlines).
A consent that is never authorised at the bank ends as `Rejected`. A Gateway Flow consent abandoned before the customer picks a bank ends as `GatewayTimeout` instead.
## Refreshing Data
To read new data later, for example each night or whenever the customer opens your app, call the data endpoints again with the same `consent_id`. The customer doesn't need to do anything: an `Authorised` consent can be used as often as you need until it expires or is revoked. Transactions booked since the customer authorised are included, up to `end_date_time` if you set one; without it, new transactions keep becoming readable.
Once the consent is `Consumed` (its `expiration_date_time` has passed) or `Revoked`, or the bank ends it (data calls then fail with `403`), create a new consent and ask the customer to authorise it again.
## Expiry and Revocation
**When consent expires:**
Create a new consent. Attempting to use an expired consent returns a 403 error (BDT412).
**Detecting expiry or revocation:**
Retrieve the consent with `GET /v1/consents/{consent_id}` to see its current status. A data call on a consent that isn't `Authorised` fails with a code that says why (for example revoked or expired); create a new consent. See [403 Forbidden](/docs/data/error-codes#_403-forbidden) for the codes.
## Consent Management
You must provide customers with the ability to view and revoke active consents through the same channel(s) where consent was created:
- **Web channel:** Provide web interface to view and revoke
- **Mobile app channel:** Provide app interface to view and revoke
- **Phone/call centre:** Provide phone-based revocation method
**Revoke (cancel) a consent:**
```http
DELETE /v1/consents/{consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Which statuses accept a revoke:**
A revoke applies to a consent that hasn't reached a final status (`GatewayAwaitingSubmission`, `AwaitingAuthorisation` or `Authorised`) and returns 204 No Content. A consent in a final status can't be revoked: the call returns an error and the status is unchanged. See [Error Codes](/docs/data/error-codes) for the codes.
# Gateway Flow (https://merchants.blinkpay.co.nz/docs/shared/flows/gateway-flow.md)
---
title: Gateway Flow
description: Strongly recommended authentication flow using BlinkPay's hosted gateway
---
# Gateway Flow
The Gateway Flow is the **strongly recommended integration method** for most use cases. BlinkPay hosts an interface that handles bank selection and customer authentication, significantly reducing your integration complexity.
This flow works for both Blink Payments (PayNow and AutoPay payments) and Blink Data (account information access), giving customers one consistent experience whichever product your application uses. For Blink Payments integrations the Gateway can also present card payment as an option alongside bank accounts, where that has been enabled for your account.
**Strongly Recommended**
Use Gateway Flow unless you have specific requirements for complete UX control. It's the fastest to implement, handles all compliance requirements automatically, and adapts as banks add new features.
## What BlinkPay Gateway Handles
The Gateway manages the complete consent authorisation experience, whether the customer is approving a payment or granting access to their account information. It presents a bank selection interface, automatically chooses the optimal authentication flow based on the customer's device, collects any required identifiers for decoupled flow, and handles the redirect orchestration between your site, the gateway, and the bank. For Blink Payments integrations, the gateway can also render a secure card payment form when card payments are enabled.
All interactions comply with Payments NZ industry guidelines, and the gateway gracefully handles errors including timeouts, rejections, and bank-specific issues.
## Why Use Gateway Flow?
Gateway Flow is the fastest path to integration. You don't need to build bank selection UI, implement compliance workflows, or handle bank-specific authentication logic. The gateway optimises the experience for each bank and device type, and automatically enables new features as banks add capabilitiesβno code changes required on your end.
## How Gateway Flow Works
The Gateway handles both [Redirect](/docs/shared/flows/redirect-flow) and [Decoupled](/docs/shared/flows/decoupled-flow) authentication flows under the hoodβcustomers select their preferred method during the consent process, and the gateway manages the technical details automatically.
## Payments and Account Access Through the Gateway
The gateway journey is the same for both products: your application creates a consent, sends the customer to the gateway, and verifies the outcome afterwards. What the customer is approving, and what your application can do once they have, differs.
| | Blink Payments | Blink Data |
| ----------------------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| What the customer approves | A payment, or a standing permission to take payments up to a limit | Sharing specific account information, for a period you set |
| Create the consent | `POST /payments/v1/single-consents`, `/quick-payments` or `/enduring-consents` on the payments host | `POST /v1/consents` on the Data host |
| What defines the consent | `amount` and `pcr`, or `maximum_amount_period` and `period` | `permissions`, `expiration_date_time`, and optional `start_date_time` / `end_date_time` for transaction history |
| What `Authorised` lets you do | Create the payment (single and enduring) or confirm it was made (quick payment) | Call the account, balance, transaction, statement and party endpoints with the `consent_id` until the consent expires or is revoked |
| What `Consumed` means | The consented action is complete: the payment has been made, or an enduring consent's period has ended | The consented action is complete: the access period has ended (`expiration_date_time` has passed). Reading data never consumes a Data consent |
The `flow` object, the `redirect_uri` the gateway returns, the return callback and the status checks below are identical for both. Where a step differs, both versions are shown.
## User Experience in Gateway
After selecting their bank, customers choose how to approve the consent. The gateway works out which options the customer's bank supports on their device and presents them; the customer picks one, and you don't need to handle any of this in your integration. For reference, the options are:
- **Internet banking website** (the redirect flow): opens the bank's site. On mobile, for ANZ, ASB and Westpac it becomes "Open in {bank} app" and opens the customer's banking app (ANZ needs a current version of goMoney); BNZ, Kiwibank and NZHL use their website on mobile too.
- **Send to {bank} app** (the decoupled flow): the customer enters an identifier such as their mobile number and approves the request in their banking app.
**Faster Experience for Repeat Customers**
To enhance the consent experience for repeat customers, provide your customer's previous `consent_id` in the gateway request. When BlinkPay provides this to the bank, the customer can be identified without re-entering their phone number, email, or banking username. They simply approve the new consent directly in their mobile banking app.
Provide the last successful Consent ID (status `Authorised` or `Consumed`) in Gateway Flow requests to activate this feature.
## Consent Timeout
Gateway consents timeout after **15 minutes** of inactivity if the customer doesn't begin the authorisation process. Once they've chosen a bank and been sent to it, that bank's own authorisation window applies instead β currently between about four and ten minutes, depending on the bank and flow.
## Integration Steps
### Step 1: Create Consent with Gateway Flow
When creating a consent, specify `gateway` as the flow type. The `flow` object is the same for every consent type on both products; the rest of the body describes what is being approved.
**Blink Payments β Single Consent (PayNow):**
```http
POST /payments/v1/single-consents HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
{
"flow": {
"detail": {
"type": "gateway",
"redirect_uri": "https://www.yourwebsite.com/payment/callback",
"flow_hint": {
"type": "redirect"
}
}
},
"amount": {
"currency": "NZD",
"total": "45.00"
},
"pcr": {
"particulars": "Invoice 123",
"code": "INV123",
"reference": "Payment"
}
}
```
**Blink Payments β Quick Payment:**
```http
POST /payments/v1/quick-payments HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
{
"flow": {
"detail": {
"type": "gateway",
"redirect_uri": "https://www.yourwebsite.com/payment/callback",
"flow_hint": {
"type": "redirect"
}
}
},
"amount": {
"currency": "NZD",
"total": "45.00"
},
"pcr": {
"particulars": "Invoice 123",
"code": "INV123",
"reference": "Payment"
}
}
```
**Blink Payments β Enduring Consent (AutoPay):**
```http
POST /payments/v1/enduring-consents HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
{
"flow": {
"detail": {
"type": "gateway",
"redirect_uri": "https://www.yourwebsite.com/payment/callback",
"flow_hint": {
"type": "redirect"
}
}
},
"maximum_amount_period": {
"currency": "NZD",
"total": "500.00"
},
"period": "monthly",
"from_timestamp": "2024-12-16T00:00:00Z",
"expiry_timestamp": "2025-12-16T00:00:00Z"
}
```
**Blink Data β Account Access Consent:**
```http
POST /v1/consents HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
{
"flow": {
"detail": {
"type": "gateway",
"redirect_uri": "https://www.yourwebsite.com/accounts/callback"
},
"flow_hint": {
"type": "redirect",
"bank": "PNZ"
}
},
"permissions": [
"ReadAccounts",
"ReadBalances",
"ReadTransactions"
],
"expiration_date_time": "2026-12-31T23:59:59+13:00",
"start_date_time": "2026-01-01T00:00:00+13:00",
"end_date_time": "2026-12-31T23:59:59+13:00"
}
```
Four fields carry the substance of a Blink Data consent, and none of them has a payment equivalent. `permissions` lists the information types the customer is being asked to share: `ReadAccounts` (account numbers, names and types), `ReadBalances`, `ReadTransactions` (history with payee details), `ReadStatements` and `ReadParty` (the account holder's name and contact details). Request only what your application needs; not every bank supports every permission, and the Data API's metadata endpoint tells you which. `expiration_date_time` sets how long access lasts; leave it out for an open-ended consent. `start_date_time` and `end_date_time` bound the transaction history the customer is sharing, independently of how long the consent itself lasts.
**Response (Blink Payments):**
```json
{
"consent_id": "3a3b7f7d-f8e6-4c3e-b2a1-5f9c8d7e6a5b",
"redirect_uri": "https://secure.blinkpay.co.nz/gateway/pay?id=3a3b7f7d-f8e6-4c3e-b2a1-5f9c8d7e6a5b"
}
```
**Blink Data:** the response has the same two fields; the gateway URL carries the consent ID as `?id=`.
**Blink Payments:** a quick payment returns `quick_payment_id` in place of `consent_id`; it is the same value as `cid` on the return URL.
Both products return the gateway URL in `redirect_uri`. The exact URL differs between products and environments, so always use the value returned rather than building one.
### Step 2: Redirect Customer to Gateway
Your backend should securely pass the `redirect_uri` to your frontend, which then redirects the customer to the gateway:
```html
Continue to your bank
```
### Step 3: Customer Experience at Gateway
When redirected to the Gateway, customers see what they are being asked to approve, select their bank, choose an authentication method, and complete authorisation at their bank. For a payment that is the amount and payee; for account access it is the permissions requested and how long they last, which the bank restates on its own consent screen. At the bank, the customer also chooses the account: the one a payment comes from, or for account access one or more accounts to share. For Blink Payments integrations with card payments enabled, the gateway also presents a card payment option.

### Step 4: Handle Return Callback
After authorisation (or cancellation), the customer is redirected back to your `redirect_uri` with the same parameters whichever product created the consent:
```
https://www.yourwebsite.com/callback?cid=3a3b7f7d-f8e6-4c3e-b2a1-5f9c8d7e6a5b
```
### Return Parameters
The parameters are the [redirect flow's return parameters](/docs/shared/flows/redirect-flow#return-parameters): `cid` always, and `status` with a display-only `error` when the outcome is not success. The gateway adds one `status` value, `pending` (the outcome is not yet known, so poll the consent), and does not pass on the bank's `error_description`.
**Always Verify via API**
There is no success value: a return with `cid` and no `status` is not confirmation. Branch on `status`, never on the text of `error`, and treat a `status` value you don't recognise as a non-success outcome. Then confirm the outcome with a server-side GET before acting on it: the return URL is not proof of payment, it can be forged, and it may never arrive.
When the customer takes the redirect option, a Blink Payments consent can also use `redirect_to_app`, where the bank returns the customer straight to your app with `code` and `state` instead of `cid`; see [Return Parameters](/docs/shared/flows/redirect-flow#return-parameters) in the redirect flow.
### Step 5: Verify Consent Status
Always verify the consent status on your backend by calling the GET endpoint for the consent before proceeding. This is the only reliable way to determine the outcome.
**Blink Payments:**
```http
GET /payments/v1/single-consents/{consent_id} HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
```
Use `/payments/v1/enduring-consents/{consent_id}` for an enduring consent, or `/payments/v1/quick-payments/{quick_payment_id}` for a quick payment, which also reports the payment itself.
For a bank quick payment, call `GET /payments/v1/quick-payments/{quick_payment_id}` as soon as the customer returns; the gateway normally does this for you, and calling it again is safe. That call completes the payment, so don't wait to see `Authorised` and don't call `POST /payments`. If it returns a 4xx or 5xx, retry the GET and read the outcome from `consent.payments[0].status`.
Don't poll while the customer is at the gateway or their bank: wait for them to return. Once they are back, read the consent straight away and, if it isn't terminal yet, poll every second. Read a payment every second until it is `AcceptedSettlementCompleted` or `Rejected`, for a minute or two at most, and stop at a terminal state.
**Blink Data:**
```http
GET /v1/consents/{consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
A gateway consent on either product moves from `GatewayAwaitingSubmission` to `AwaitingAuthorisation` when the customer picks a bank, then to `Authorised` or `Rejected`. `GatewayTimeout` means the customer never chose a bank within the gateway's window. For a Data consent, `Authorised` means the customer approved: call the account endpoints with its `consent_id`, for example `GET /v1/accounts?consent_id={consent_id}`. `Rejected` or `GatewayTimeout` means they didn't; show a clear message and let them try again.
## Flow Hints
The `flow_hint` parameter tells Gateway which authentication method to optimise for. Where it goes differs by product: in a Blink Payments request it sits inside `flow.detail`, and in a Blink Data request it sits directly under `flow`, next to `detail`.
### Redirect Flow Hint
```json
{
"flow_hint": {
"type": "redirect",
"bank": "PNZ"
}
}
```
Best for desktop users and browser-based checkout flows. The gateway offers redirect-based authentication, opening the bank website, or on mobile the banking app for banks that support it. A hint that names a bank pre-selects that bank, so the customer skips bank selection.
**Blink Data:** every flow hint must name a `bank`; a hint without one is rejected with 400 `BDT260`. A hint naming a bank your organisation isn't set up for is ignored, and the customer chooses a bank in the gateway as usual.
### Decoupled Flow Hint
```json
{
"flow_hint": {
"type": "decoupled",
"bank": "BNZ",
"identifier_type": "consent_id",
"identifier_value": "5c2f9a4e-8b7d-4e3a-9f1c-2d6b8e4a7c31"
}
}
```
Best for returning customers with previous consents or when customers prefer app authorisation. The gateway can pre-fill bank selection and the customer authorises on their mobile device. For Kiwibank and NZHL the gateway preselects the bank's website on every device, with "Send to {bank} app" available as a switch; a decoupled flow hint for one of those banks preselects the app option instead.
**Smart Flow Selection**
If you don't specify a flow hint, Gateway will intelligently select the best option based on device type and customer history.
## Gateway Features
The gateway provides a polished, accessible bank selection interface with visual logos and search capability. It's fully responsive and WCAG 2.1 AA compliant.
Beyond bank selection, it chooses between the redirect and decoupled flows for the customer's bank and device, and on mobile it opens the customer's banking app where the bank supports it.
**Blink Payments only:** the gateway shows a confirmation screen before sending the customer to the bank, with who they are paying, the amount and the particulars, code and reference (for AutoPay, the agreement details). It knows each bank's payment limit and warns the customer when the amount is over it, so they can choose another bank, and it tells ANZ customers when Voice ID is needed for a payment over $10,000.
**Blink Data:** the customer sees the permissions your application is requesting and how long access lasts. There are no payment limits.
### Automatic Flow Optimisation
The gateway preselects an authentication option for each bank; where the bank offers both, the customer can switch:
| Bank | Mobile Device | Desktop Device |
| ---------------------- | -------------------------------- | ---------------------------------------------------- |
| **ANZ** | Open in ANZ app | Send to ANZ app (the website option shows a QR code) |
| **ASB** | Open in ASB app | Send to ASB app (the website option shows a QR code) |
| **BNZ** | Send to BNZ app | Customer chooses: website or Send to BNZ app |
| **Kiwibank**, **NZHL** | Internet banking website (the customer can switch to Send to app, with their mobile number) | Internet banking website (the customer can switch to Send to app) |
| **Westpac** | Open in Westpac app | Customer chooses: website or Send to Westpac app |
A decoupled `flow_hint` for the chosen bank generally preselects "Send to app" instead; ANZ on mobile still opens the app. Every bank's "Send to app" option takes the identifiers listed under [Decoupled Flow Identifiers](/docs/shared/bank-coverage#decoupled-flow-identifiers); for Kiwibank and NZHL that is a mobile number or a previous consent ID.
### Error Handling
## Advanced Configuration
### Custom Branding
You can display your brand logo on the Gateway. Configure co-branding options in the [BlinkPay Client Portal](https://merchants.blinkpay.co.nz/).
### Tracking Your Own References
To tie a consent to your order, store the `consent_id` (or `quick_payment_id`) against the order on your server before sending the customer to the gateway. You can also add your own query string to the `redirect_uri`, such as `?order=123`: query strings are ignored when the URI is checked against your whitelist, and they come back with the customer. For a payment, the PCR fields (particulars, code and reference) travel with the payment itself.
Neither product's consent takes a metadata object of your own. A Blink Payments consent carries your references in the PCR fields; a Blink Data consent has no equivalent, so keep the link between the consent and your records on your server.
## Gateway URLs
The gateway URL is returned in the `redirect_uri` field of the consent responseβalways use the URL provided by the API rather than hardcoding.
**Testing**
Use sandbox to test the complete customer journey without real banking credentials. Select "PNZ" as the mock bank.
## Troubleshooting
For common issues and solutions, see the [Troubleshooting Guide](/docs/shared/help/troubleshooting).
**Quick checks:**
- Gateway not loading? Verify your `redirect_uri` is correct and check for ad blockers
- Customer redirected back immediately? Check consent statusβmay be `Rejected` or `GatewayTimeout`
- Wrong redirect location? Ensure URL is fully qualified (includes https://) and whitelisted
## Best Practices
**Redirect URIs:** Use HTTPS for web return URLs (production rejects `http://` and `localhost`; a mobile app may use a custom scheme), include order/transaction IDs for tracking, and handle both success and failure scenarios. Never trust query parameters aloneβalways verify consent status on your backend. If you use multiple subdomains, consider registering a wildcard entry (e.g., `https://*.example.com/callback`) rather than individual URIs. See [Redirect URI Whitelisting](/docs/shared/authentication#redirect-uri-whitelisting) for details.
**User Experience:** Show loading states while creating consents, provide clear instructions before redirect, and offer "Try again" options for failures. Don't use iFrames (not supported) or popups (may be blocked).
# Redirect Flow (https://merchants.blinkpay.co.nz/docs/shared/flows/redirect-flow.md)
---
title: Redirect Flow
description: Learn about direct bank redirect integration with BlinkPay
---
# Redirect Flow
Redirect Flow (also known as Pay by Bank) is a browser-based authentication method where customers are redirected directly to their bank and then back to your application once they have authorised. Despite the browser-based name, it works on mobile as well as desktop: on a phone, the bank's website opens in the browser, or the bank's app opens directly where the bank supports it. It works the same way for a Blink Payments payment and for a Blink Data account access consent: only what the customer approves at the bank differs.
## Overview
In Redirect Flow, the customer:
1. Starts a payment, or a request to share account information, on your website/app
2. Selects their bank (you provide the UI)
3. Gets redirected to their bank's website or mobile app
4. Authenticates and authorises the payment or the account access
5. Gets redirected back to your website/app

## When to Use Redirect Flow
Use Redirect Flow when you need full control over the bank selection UI, custom branding throughout the flow, or specific UX patterns for your use case.
For most integrations, [Gateway Flow](/docs/shared/flows/gateway-flow) is recommendedβit handles bank selection, device optimisation, and compliance automatically.
## Bank Support
### Redirect Flow Behaviour by Bank
| Bank | Desktop | Mobile |
| ------------ | ---------------------------- | --------------------- |
| **ANZ** | Bank website (shows QR code) | Opens the ANZ app |
| **ASB** | Bank website (shows QR code) | Opens the ASB app |
| **BNZ** | Bank website | Bank website |
| **Kiwibank** | Bank website | Bank website |
| **NZHL** | Bank website | Bank website |
| **Westpac** | Bank website | Opens the Westpac app |
**Mobile Experience**
On mobile, ANZ, ASB and Westpac open the customer's banking app to authorise (ANZ needs a current version of goMoney). That is not the same as returning the customer to your app afterwards: by default the bank sends the customer back to your `redirect_uri` in the browser. Returning straight to your app is a separate, Blink Payments-only option described under [Return Parameters](#return-parameters).
## Integration Steps
### Step 1: Build Bank Selection UI
Create a UI for customers to select their bank:
```html
Select Your Bank
```
**Bank Identifiers:**
- ANZ: `"ANZ"`
- ASB: `"ASB"`
- BNZ: `"BNZ"`
- Kiwibank: `"Kiwibank"`
- NZHL: `"NZHL"`
- Westpac: `"Westpac"`
The same identifiers are used by both products. Which banks are available to your application for payments and for account information is set out in [Bank Coverage](/docs/shared/bank-coverage).
### Step 2: Create Consent with Redirect Flow
When the customer selects a bank, create a consent specifying redirect flow. The `flow` object, with the bank and your `redirect_uri`, is the same for every consent type on both products.
**Blink Payments β Single Consent (PayNow):**
```http
POST /payments/v1/single-consents HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
{
"flow": {
"detail": {
"type": "redirect",
"bank": "ANZ",
"redirect_uri": "https://www.yourwebsite.com/payment/callback"
}
},
"amount": {
"currency": "NZD",
"total": "75.00"
},
"pcr": {
"particulars": "Invoice",
"code": "12345",
"reference": "Payment"
}
}
```
**Blink Payments β Quick Payment:**
```http
POST /payments/v1/quick-payments HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
{
"flow": {
"detail": {
"type": "redirect",
"bank": "ANZ",
"redirect_uri": "https://www.yourwebsite.com/payment/callback"
}
},
"amount": {
"currency": "NZD",
"total": "75.00"
},
"pcr": {
"particulars": "Invoice",
"code": "12345",
"reference": "Payment"
}
}
```
**Blink Payments β Enduring Consent (AutoPay):**
```http
POST /payments/v1/enduring-consents HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
{
"flow": {
"detail": {
"type": "redirect",
"bank": "ANZ",
"redirect_uri": "https://www.yourwebsite.com/payment/callback"
}
},
"maximum_amount_period": {
"currency": "NZD",
"total": "500.00"
},
"period": "monthly",
"from_timestamp": "2024-12-16T00:00:00Z"
}
```
**Blink Data β Account Access Consent:**
```http
POST /v1/consents HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
{
"flow": {
"detail": {
"type": "redirect",
"bank": "ANZ",
"redirect_uri": "https://www.yourwebsite.com/accounts/callback"
}
},
"permissions": [
"ReadAccounts",
"ReadBalances",
"ReadTransactions"
],
"expiration_date_time": "2026-12-31T23:59:59+13:00",
"start_date_time": "2026-01-01T00:00:00+13:00",
"end_date_time": "2026-12-31T23:59:59+13:00"
}
```
Four fields carry the substance of a Blink Data consent, and none of them has a payment equivalent. `permissions` lists the information types the customer is being asked to share: `ReadAccounts` (account numbers, names and types), `ReadBalances`, `ReadTransactions` (history with payee details), `ReadStatements` and `ReadParty` (the account holder's name and contact details). Request only what your application needs; not every bank supports every permission, and the Data API's metadata endpoint tells you which. `expiration_date_time` sets how long access lasts; leave it out for an open-ended consent. `start_date_time` and `end_date_time` bound the transaction history the customer is sharing, independently of how long the consent itself lasts.
**Response (both products):**
```json
{
"consent_id": "8f7e6d5c-4b3a-2c1b-0a09-8f7e6d5c4b3a",
"redirect_uri": "https://api-nomatls.apicentre.middleware.co.nz/middleware-nz-ais/open-banking-nz/v2.3/consents/..."
}
```
A quick payment returns `quick_payment_id` in place of `consent_id`; it is the same value as `cid` on the return URL.
**Bank-Specific URLs**
The `redirect_uri` returned by BlinkPay is the bank's authorisation URL. It's different for each bank, and for each product, and will redirect the customer to their bank's platform.
### Step 3: Redirect Customer to Bank
Your backend should securely pass the `redirect_uri` to your frontend, which then immediately redirects the customer to the bank's authorisation URL.
### Step 4: Customer Authorisation at Bank
The customer is now at their bank's platform, where they sign in with their banking credentials, review what they are approving, choose the account, and confirm. For a payment they see the amount and payee, and choose the account the money comes from. For account access they see the permissions requested and the period of access, and choose the accounts the consent covers. They can usually select more than one, which is why `GET /v1/accounts` can return several accounts.
On desktop, every bank opens its internet banking website; ANZ and ASB show a QR code there for the customer to scan with their banking app. On mobile, ANZ, ASB and Westpac open their banking app, while BNZ, Kiwibank and NZHL use their website on all devices.
### Step 5: Handle Return Callback
After authorisation, or when the customer cancels, declines or runs out of time, the customer is redirected back to your `redirect_uri` with the parameters described in [Return Parameters](#return-parameters):
```
https://www.yourwebsite.com/order/12345/complete?cid=8f7e6d5c-4b3a-2c1b-0a09-8f7e6d5c4b3a
```
### Return Parameters
BlinkPay appends these query parameters to your `redirect_uri`. They are the same for Blink Payments and Blink Data.
| Parameter | When | Meaning |
| ------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `cid` | Always | The consent ID. For a quick payment this is also the `quick_payment_id`. |
| `status` | Only when the outcome is not success | `cancelled` or `rejected` (the customer or bank declined), `timeout` (completing the authorisation timed out) or `error` (a technical failure). Treat all four as not authorised. |
| `error` | With `status` | A customer-safe message you can display. Its wording changes without notice. |
| `error_description` | Sometimes, with `status` | Further detail from the bank, when available. |
There is no success value: a return with `cid` and no `status` means the customer finished at the bank, not that the consent was authorised or a payment made. Branch on `status`, never on the text of `error`, and treat a `status` value you don't recognise as a non-success outcome. Whatever the parameters say, confirm the outcome with a server-side GET before you act on it: the return URL is not proof of payment, it can be forged, and it may never arrive if the customer closes the browser. For a quick payment the parameters report only the consent's outcome, because the debit is submitted after this return (see Step 6).
The [gateway flow](/docs/shared/flows/gateway-flow#return-parameters) returns the same parameters, with one extra `status` value, `pending`, and without `error_description`.
**Blink Payments only: returning straight to your app.** With `redirect_to_app`, the bank returns the customer straight to your app with its own `code` and `state` instead of `cid`; see [App-to-App Flow](/docs/shared/flows/app-to-app).
### Step 6: Verify and Continue
Always verify the outcome on your backend by calling the GET endpoint for the consent. A redirect flow consent on either product moves from `AwaitingAuthorisation` to `Authorised` or `Rejected`. What happens next depends on the consent type.
**Blink Payments, quick payment:** don't wait for `Authorised`, and don't call `POST /payments`. Call `GET /payments/v1/quick-payments/{quick_payment_id}` (the `cid`) as soon as the customer returns; that call completes the payment, and calling it again is safe. If it returns a 4xx or 5xx, retry the GET rather than treating the payment as failed. Then keep reading the quick payment until the consent is `Consumed` or `Rejected`, and take the payment's outcome from `payments[0].status`.
**Blink Payments, single and enduring consents:** fetch `GET /payments/v1/single-consents/{consent_id}` (or the enduring-consents equivalent) on the payments host, and continue only when it is `Authorised`. Then create the payment: for a single consent send only the `consent_id`; for an enduring consent include the amount and PCR. `Consumed` means the consented action is complete: the payment has been made, or an enduring consent's period has ended. Always read the payment's own status as well, because a single consent whose payment the bank rejected after accepting it is also `Consumed`.
**Polling:** don't poll while the customer is at their bank; wait for them to return, then read the consent and, if it isn't terminal yet, poll every second. Once a payment exists, read it every second until it is `AcceptedSettlementCompleted` or `Rejected`, for a minute or two at most. Stop at a terminal state, and run a periodic sweep for any consents or payments you lost track of.
**Blink Data:** fetch `GET /v1/consents/{consent_id}` on the Data host. `Authorised` means the customer approved: call the account endpoints with the `consent_id`, starting with `GET /v1/accounts?consent_id={consent_id}`. `Rejected` means they didn't; show a clear message and let them try again.
## Mobile App Integration
For mobile apps, open the bank page in an in-app browser: `SFSafariViewController` or `ASWebAuthenticationSession` on iOS, Chrome Custom Tabs on Android. Use a custom URL scheme or a universal link / App Link as your `redirect_uri`, and test opening the banking apps on real devices.
For complete implementation guidance, see the [Mobile Integration Guide](/docs/shared/flows/mobile-integration).
**Registering Your App's Return Link**
A custom scheme such as `myapp://callback` (it needs a host part; `myapp:callback` is rejected) or a universal link / Android App Link can be your `redirect_uri`. Register it yourself in the BlinkPay portal under **Settings > API Settings**, in the Whitelisted Redirect URLs for each environment. Banks don't need to whitelist it. Contact BlinkPay support only if you want app-to-app return (`redirect_to_app`, Blink Payments only). See [Register Your Return Link](/docs/shared/flows/mobile-integration#register-your-return-link).
**iFrames and Embedded WebViews Not Supported**
iFrames cannot be used, because banks' anti-framing policies block them, and embedded WebViews (`WKWebView`, Android `WebView`) are not supported. Use an in-app browser; see [Mobile Integration](/docs/shared/flows/mobile-integration).
## Troubleshooting
For common issues and solutions, see the [Troubleshooting Guide](/docs/shared/help/troubleshooting).
**Quick checks:**
- Redirect loop? Ensure callback handling doesn't re-initiate payment
- Bank page not loading? Check bank is operational and customer has online banking enabled
- Deep link not working? Ensure banking app is installed and up to date
- Consent not authorised? Customer may have cancelled or session timed outβcheck the specific status
## Best Practices
**Bank Selection UI:** Show every bank available to your application, with official logos. Make buttons touch-friendly, show loading states during redirect, and always provide a cancel option.
**Redirect Handling:** Always validate consent status on your backendβnever trust query parameters alone. Handle both outcomes (`Authorised` and `Rejected`) and provide clear retry options. Status values are case-sensitive.
**Mobile:** Use an in-app browser (Custom Tabs, SFSafariViewController or ASWebAuthenticationSession), never an embedded WebView. Test on real devices, and if the banking app doesn't open, let the customer continue on the bank's website.
## Testing
Test in sandbox with the mock bank "PNZ", username `user01` and password `password`, on both products. Before going live, test with real banks across multiple devices and browsers, using small amounts ($0.01) for payments and your own accounts for account access.
[View going live checklist β](/docs/shared/help/going-live)
# Decoupled Flow (https://merchants.blinkpay.co.nz/docs/shared/flows/decoupled-flow.md)
---
title: Decoupled Flow
description: Learn about mobile authorisation with BlinkPay's Decoupled Flow
---
# Decoupled Flow
Decoupled Flow (also known as Pay by Mobile) allows customers to authorise on a different device or at a different time using their banking mobile app. The bank sends the request straight to the customer's banking app, using an identifier such as their mobile number. Some banks send a push notification; with others the customer opens the app and goes to its notifications or messages section to approve. It works for a Blink Payments payment and for a Blink Data account access consent alike: the bank sends the same kind of authorisation request to the customer's app, and only what they are approving differs.
## Overview
Decoupled Flow separates the authorisation process from the initial request:
1. Customer starts a payment, or a request to share account information, on your website or app
2. You create a consent with decoupled flow
3. Customer receives an authorisation request on their mobile device
4. Customer authorises using their banking app
5. You poll for authorisation status, then create the payment or start retrieving data

## When to Use Decoupled Flow
Decoupled Flow is best for scenarios where authorisation happens asynchronouslyβbill payment systems, desktop-initiated transfers where customers prefer mobile authorisation, recurring payment setup, and returning customers with saved payment methods.
For most integrations, [Gateway Flow](/docs/shared/flows/gateway-flow) is recommended as it handles flow selection automatically. If customers are already on mobile, consider the [Redirect Flow](/docs/shared/flows/redirect-flow), which opens the banking app on mobile for banks that support it.
## Bank Support
### Decoupled Flow Support by Bank
| Bank | Identifier | How the Customer Sees the Request |
| ------------ | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| **ANZ** | Mobile number or consent ID | goMoney notification on iOS; on Android, the customer opens goMoney to approve |
| **ASB** | Mobile number or consent ID | Push notification, if enabled in app settings |
| **BNZ** | Mobile number, email or consent ID | Push notification |
| **Kiwibank** | Mobile number or consent ID | No push notification: the customer opens the app to approve |
| **NZHL** | Mobile number or consent ID | Tell the customer to open their app if no notification arrives |
| **Westpac** | Access Number (banking username) or consent ID | Push notification, if enabled in app settings |
A consent ID here means the ID of a consent your application created earlier for the same customer at the same bank, and which was authorised; see the Using Previous Consents tip under Step 1.
**When There Is No Push Notification**
On your waiting screen, always tell the customer to open their banking app and look for the request if no notification arrives: Kiwibank sends none, ANZ sends none on Android, and customers may have notifications turned off.
## Integration Steps
### Step 1: Collect the Customer's Identifier
Decoupled flow needs an identifier that tells the bank which customer to send the request to. You send it as `identifier_type` and `identifier_value`:
| Bank | Accepted `identifier_type` values |
| ------------ | ------------------------------------------------------------------------- |
| **ANZ** | `mobile_number`, `consent_id` |
| **ASB** | `mobile_number`, `consent_id` |
| **BNZ** | `mobile_number`, `email`, `consent_id` |
| **Kiwibank** | `mobile_number`, `consent_id` |
| **NZHL** | `mobile_number`, `consent_id` |
| **Westpac** | `banking_username` (the customer's Westpac Access Number), `consent_id` |
Send the customer's mobile number as `mobile_number` (Westpac takes `banking_username` instead); use `mobile_number`, not `phone_number`. An identifier type the bank doesn't accept is rejected when you create the consent. An identifier value the bank doesn't recognise, such as a mobile number not registered for its app, is not an error: the consent is created, stays `AwaitingAuthorisation` and becomes `Rejected` when the bank's window ends, exactly as if the customer had ignored the request.
**Using Previous Consents**
For a returning customer, set `identifier_type` to `consent_id` and `identifier_value` to the ID of an earlier consent your application created for that customer at the same bank. It must be `Authorised` or `Consumed`, not revoked, and the customer then enters nothing. If the bank rejects an old consent as an identifier, fall back to the customer's usual identifier.
### Step 2: Create Consent with Decoupled Flow
The `flow` object, with the bank, the identifier and an optional `callback_url`, is the same for every consent type on both products.
**Blink Payments β Single Consent (PayNow):**
```http
POST /payments/v1/single-consents HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
{
"flow": {
"detail": {
"type": "decoupled",
"bank": "ANZ",
"identifier_type": "mobile_number",
"identifier_value": "+64212345678",
"callback_url": "https://www.yourwebsite.com/blinkpay/consent-callback"
}
},
"amount": {
"currency": "NZD",
"total": "45.00"
},
"pcr": {
"particulars": "Invoice",
"code": "12345",
"reference": "Payment"
}
}
```
**Blink Payments β Quick Payment:**
```http
POST /payments/v1/quick-payments HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
{
"flow": {
"detail": {
"type": "decoupled",
"bank": "ANZ",
"identifier_type": "mobile_number",
"identifier_value": "+64212345678",
"callback_url": "https://www.yourwebsite.com/blinkpay/consent-callback"
}
},
"amount": {
"currency": "NZD",
"total": "45.00"
},
"pcr": {
"particulars": "Invoice",
"code": "12345",
"reference": "Payment"
}
}
```
**Blink Payments β Enduring Consent (AutoPay):**
```http
POST /payments/v1/enduring-consents HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
{
"flow": {
"detail": {
"type": "decoupled",
"bank": "Westpac",
"identifier_type": "banking_username",
"identifier_value": "349373893",
"callback_url": "https://www.yourwebsite.com/blinkpay/consent-callback"
}
},
"maximum_amount_period": {
"currency": "NZD",
"total": "500.00"
},
"period": "monthly",
"from_timestamp": "2024-12-16T00:00:00Z"
}
```
**Blink Data β Account Access Consent:**
```http
POST /v1/consents HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
{
"flow": {
"detail": {
"type": "decoupled",
"bank": "BNZ",
"identifier_type": "mobile_number",
"identifier_value": "+64212345678",
"callback_url": "https://www.yourwebsite.com/blinkpay/consent-callback"
}
},
"permissions": [
"ReadAccounts",
"ReadBalances",
"ReadTransactions"
],
"expiration_date_time": "2026-12-31T23:59:59+13:00",
"start_date_time": "2026-01-01T00:00:00+13:00",
"end_date_time": "2026-12-31T23:59:59+13:00"
}
```
Four fields carry the substance of a Blink Data consent, and none of them has a payment equivalent. `permissions` lists the information types the customer is being asked to share: `ReadAccounts` (account numbers, names and types), `ReadBalances`, `ReadTransactions` (history with payee details), `ReadStatements` and `ReadParty` (the account holder's name and contact details). Request only what your application needs; not every bank supports every permission, and the Data API's metadata endpoint tells you which. `expiration_date_time` sets how long access lasts; leave it out for an open-ended consent. `start_date_time` and `end_date_time` bound the transaction history the customer is sharing, independently of how long the consent itself lasts.
**Response (both products):**
```json
{
"consent_id": "9f8e7d6c-5b4a-3c2b-1a09-8f7e6d5c4b3a"
}
```
A quick payment returns `quick_payment_id` in place of `consent_id`; it is the same value as the `cid` your `callback_url` receives.
**No Redirect URI**
Unlike redirect and gateway flows, decoupled flow does not return a `redirect_uri`. The customer authorises on their mobile device asynchronously.
### Step 3: Show Status to Customer
Display a waiting screen informing the customer that authorisation is pending. Restate what they are about to approve: the amount and reference for a payment, or the information being shared and for how long for account access.
```html
Waiting for Authorisation
Please check your ANZ mobile app to authorise this request.
You should receive a push notification on your mobile device. If not, please open your banking app and check for pending authorisations.
Open your banking app and look in its notifications or messages section to approve this request.
Amount: $45.00
Reference: Invoice 12345
Sharing: accounts, balances and transactions
Until: 31 December 2026
```
### Step 4: Poll for Authorisation Status
Nothing comes back to you in this flow, so poll the consent every 5 seconds until it reaches a terminal state. If you set a `callback_url`, wait for the callback instead and read the consent when it arrives, polling every second if it isn't terminal yet.
Bound your polling by the bank's own window rather than a flat figure. Decoupled windows are set per bank and currently range from about four minutes to ten, so a fixed five-minute cutoff abandons consents at some banks while they're still live. Read the bank's window from `features.decoupled_flow.request_timeout` on the Blink Payments [metadata endpoint](/docs/shared/reference/metadata). The Blink Data metadata endpoint has no timeouts, so for account access consents use the per-bank windows in [Bank Coverage](/docs/shared/bank-coverage). If you do stop polling at a cutoff of your own, reconcile the consent afterwards rather than assuming it failed.
### Step 5: Continue Once Authorised
A decoupled consent on either product moves from `AwaitingAuthorisation` to `Authorised`, or to `Rejected` if the customer declines or the bank's window elapses. Handle `Rejected` and `Revoked` with clear messages and a way to try again.
**Blink Payments, quick payment:** there is nothing to initiate. BlinkPay submits the payment as soon as the customer authorises, so don't call `POST /payments`. Read `GET /payments/v1/quick-payments/{quick_payment_id}` until the consent is `Consumed` or `Rejected`, and take the payment's outcome from `consent.payments[0].status`.
**Blink Payments, single and enduring consents:** once `Authorised`, create the payment with `POST /payments/v1/payments`. For a single consent send only the `consent_id`, because the amount is already set; for an enduring consent include the amount and PCR. Then read the payment every second until it is `AcceptedSettlementCompleted` or `Rejected`. `Consumed` means the consented action is complete: the payment has been made, or an enduring consent's period has ended. Always read the payment's own status as well, because a single consent whose payment the bank rejected after accepting it is also `Consumed`.
**Blink Data:** `Authorised` means the customer approved, and there is nothing further to complete. Call the account endpoints with the `consent_id`, starting with `GET /v1/accounts?consent_id={consent_id}` on the Data host.
## Consent Authorisation Callback
To avoid polling continuously, include a `callback_url` when creating the consent. When the customer authorises, Blink fires an unsigned HTTP `GET` to that URL with the consent ID appended as a query parameter:
```
GET https://www.yourwebsite.com/blinkpay/consent-callback?cid=9f8e7d6c-5b4a-3c2b-1a09-8f7e6d5c4b3a
```
Treat the callback as a "wake-up" signal β it tells you something changed on this consent, but carries no body, no signature, and no status field. Always re-fetch the consent to determine its actual state before acting. Which endpoint you re-fetch from depends on the product:
- **Payments** β `GET /payments/v1/enduring-consents/{consent_id}`, `GET /payments/v1/single-consents/{consent_id}` for a single consent, or `GET /payments/v1/quick-payments/{cid}` for a quick payment (the `cid` is the `quick_payment_id`). For a quick payment, the payment is submitted at authorisation; read its outcome from the quick payment.
- **Data** β `GET /v1/consents/{consent_id}` on the Data API host. There is no `/payments/v1/...` path on the Data API.
The callback fires when the decoupled authorisation reaches its outcome β when the consent is authorised, and also when it is rejected or the bank's approval window times out. It carries no status field, so the ping alone never tells you which of those happened.
Do not treat it as complete coverage of the consent's lifecycle: a consent revoked after it was authorised does not produce one, and delivery is not guaranteed in any case. **Blink Data:** the callback is only sent for consents created with the decoupled flow, never for a gateway consent, even when the customer chooses the decoupled option in the gateway. Polling remains how you detect terminal states.
Delivery is **at-least-once, and not guaranteed**. Blink may retry up to three times when your endpoint returns a 5xx, so the same `cid` can arrive more than once β make handling it repeatedly safe. Equally, a callback that fails because your endpoint is unreachable, slow, or keeps erroring may not arrive at all. Treat polling as the authoritative mechanism and the callback purely as an optimisation that lets you poll sooner.
**The callback is unsigned β don't trust the `cid` blindly**
Anyone who learns your `callback_url` can spoof a ping. Authentication of the state change comes from the follow-up `GET` you make against Blink's API using your own credentials, not from the callback itself. Never act on `cid` alone β always re-fetch.
This callback is the only notification for a decoupled consent; read the consent or payment to learn its state. Fixed Recurring Payment webhooks (Blink Payments only) are a separate mechanism: signed POSTs with a JSON event payload, with a different security model and contract.
## Customer Experience
For ASB, BNZ and Westpac, and ANZ on iOS, customers receive a notification on their mobile device, tap to open their banking app, review the request, choose the account (for account access, one or more), and authorise, then return to your website.
Where no notification arrives (Kiwibank, ANZ on Android, or notifications turned off), customers must open their app themselves and find the request in its notifications or messages section. Tell them so clearly on your waiting screen.
## Troubleshooting
For common issues and solutions, see the [Troubleshooting Guide](/docs/shared/help/troubleshooting).
**Quick checks:**
- No push notification? Check the bank sends one (Kiwibank doesn't; ANZ does on iOS only) and that the customer has notifications enabled
- Authorisation never completes? Verify the identifier is correct (an unregistered mobile number isn't reported as an error; the consent just times out), and check the consent hasn't passed the bank's authorisation window
- Wrong bank or account? Verify the bank and the identifier point to the correct customer
- Multiple pending authorisations? Avoid creating duplicate consents (send an `idempotency-key` when you create one); for Blink Payments, also put a unique reference in the PCR fields
## Best Practices
**Identifiers:** Check mobile numbers look valid before use and store previous consent IDs for returning customers. Never use invalid identifiers or store them insecurely.
**Polling:** Read the consent every 5 seconds (or wait for the callback), then a payment every second until it is `AcceptedSettlementCompleted` or `Rejected`, for a minute or two at most. Stop on a terminal status, and run a periodic sweep for anything you lost track of.
**User Experience:** Show clear, bank-specific instructionsβdifferentiate between banks with and without push notifications. Provide a cancel option and progress indicator.
## Testing
Test in sandbox with `bank: "PNZ"` and the mobile number `+64-259531933`, which auto-approves on both products (in the gateway, choose **Send to PNZ app**). Other numbers are accepted, but the consent is never authorised: it stays `AwaitingAuthorisation` until the authorisation window ends, then becomes `Rejected`. They don't simulate a decline. To test revocation, let the consent auto-approve, then revoke it with the DELETE endpoint. Before going live, test with real banks using your own mobile number to verify push notifications and manual authorisation flows.
# Mobile Integration (https://merchants.blinkpay.co.nz/docs/shared/flows/mobile-integration.md)
---
title: Mobile Integration
description: Guide to integrating BlinkPay in mobile applications
---
# Mobile Integration
BlinkPay supports mobile applications on iOS and Android. This guide covers the architecture a mobile integration needs, how to open the bank's pages from your app, how to bring the customer back to your app, and how to find out the outcome reliably.
**Embedded WebViews and iFrames Not Supported**
Don't show BlinkPay or bank pages in an embedded WebView (`WKWebView` on iOS, `WebView` on Android) or an iframe. Use SFSafariViewController or ASWebAuthenticationSession on iOS and Chrome Custom Tabs on Android, as described in [Opening the Bank Pages](/docs/shared/flows/mobile-integration#opening-the-bank-pages).
## Recommended Approach
Use the Gateway Flow with an in-app browser and a registered return link. The gateway handles bank selection, flow routing and platform detection, and on mobile it opens the customer's banking app where the bank supports it. When the customer has finished, it sends them back to your app through the return link you register (see [Register Your Return Link](/docs/shared/flows/mobile-integration#register-your-return-link)).
## Architecture
A mobile app needs a backend server of your own. Your client ID and client secret live only on that backend, which requests access tokens and makes every BlinkPay API call; they must never be compiled into the app, where anyone can extract them. The backend also decides the amount and the payment reference details (PCR) from its own record of the order, rather than accepting them from the app, because a modified app could send any amount it likes.
The app authenticates to your backend in the same way as the rest of your app does, with your own user sessions or login tokens. It never holds BlinkPay credentials or BlinkPay access tokens. See [Credential and Integration Security](/docs/shared/standards/credential-security) for how to store and rotate the credentials on the backend.
1. The app asks your backend to start a payment (or a Blink Data consent) for an order.
2. Your backend creates the consent or quick payment with the BlinkPay API, stores the returned ID against the order, and returns the `redirect_uri` from the response to the app.
3. The app opens that URL in an in-app browser.
4. The customer authorises at their bank, often in the bank's own app.
5. The customer comes back to your app through your return link, which carries `cid` (the consent ID; for a quick payment, the `quick_payment_id`).
6. The app asks your backend for the outcome, and your backend reads it from the BlinkPay API.
```
Mobile App β Your Backend β BlinkPay API
β
redirect_uri returned to app
β
SFSafariViewController / Chrome Custom Tabs β Gateway β Banking App
β
Return link (cid) β Mobile App β Your Backend β GET the consent
```
## Reference Implementation
The [BlinkPay Flutter Demo](https://github.com/BlinkPay/BlinkPay-Flutter-Demo) is a complete working example. It is a reference implementation: with a little help from an AI assistant it translates readily to other languages and frameworks, so use it as the reference whatever you build in. It uses the gateway flow with single consents (one-off payments) and enduring consents (recurring payments). Its `server/` folder is a small backend proxy that holds the client credentials and obtains the access tokens, so the app contains no BlinkPay secrets. The app opens the gateway with `flutter_custom_tabs` (Chrome Custom Tabs on Android, SFSafariViewController on iOS) and receives the custom-scheme return link `blinkpaydemo://callback` with `app_links`. Its README's [Deep Linking Setup](https://github.com/BlinkPay/BlinkPay-Flutter-Demo#deep-linking-setup) section shows the Android manifest, the iOS `Info.plist` entries and the iOS code that closes the in-app browser.
**Reference Implementation Only**
The Flutter Demo is a reference implementation, not a supported SDK package. It takes shortcuts that keep the demo small and must not be copied into a real app: the app sets the amount and PCR and the proxy passes them through unchecked (your backend must set them); the app authenticates to the proxy with a fixed demo API key built into the app (use your own user sessions); and the consent ID is held only in the app's memory (store it on your backend, so that the outcome can still be found if the app is closed).
## Opening the Bank Pages
Open the gateway, or the bank's authorisation page in the redirect flow, in the platform's in-app browser: SFSafariViewController or ASWebAuthenticationSession on iOS, and Chrome Custom Tabs on Android. These run the page in a real browser that your app cannot read or script, so the customer's banking credentials never pass through your app. Banks' anti-framing policies block their pages from being embedded, and in a real browser the customer can see the bank's genuine address before signing in.
| Platform | In-app browser (examples) | Notes |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| iOS | `SFSafariViewController`, or `ASWebAuthenticationSession` | SFSafariViewController does not close itself when your return link arrives; dismiss it yourself |
| Android | Chrome Custom Tabs (AndroidX Browser library) | Falls back to the default browser on devices without a Custom Tabs browser |
| Flutter | `flutter_custom_tabs` (used by the Flutter Demo) | Pair it with a link handler such as `app_links` |
| React Native | `react-native-inappbrowser-reborn` (`InAppBrowser.open`, or `openAuth` for its auth-session mode), or Expo's `expo-web-browser` (`openBrowserAsync`, or `openAuthSessionAsync`) | `open` and `openBrowserAsync` use SFSafariViewController and Custom Tabs; close them with `InAppBrowser.close` or `dismissBrowser` (iOS) when your return link arrives |
These packages are examples, not endorsements; any library that uses the platform's in-app browser works. The auth-session modes (ASWebAuthenticationSession, `openAuth`, `openAuthSessionAsync`) close themselves when the return link arrives, but on iOS they first show a system prompt saying that your app wants to use the website to sign in, which can confuse a customer who is paying rather than signing in. SFSafariViewController and its equivalents show no such prompt.
## Register Your Return Link
Your return link is the `redirect_uri` you send when you create a consent or quick payment with the gateway or redirect flow. On mobile it is a link that opens your app. Two kinds work:
| Kind | Example | Notes |
| ----------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Custom scheme | `myapp://callback` | Simple to set up, and what the Flutter Demo uses. Must include a host part: `myapp:callback` is rejected. Any app can claim the same scheme, which is one more reason to confirm every outcome on your backend |
| Universal link (iOS) / App Link (Android) | `https://app.example.com/blinkpay/return` | An HTTPS link verified as belonging to your app, which needs the `apple-app-site-association` (iOS) and `assetlinks.json` (Android) files hosted on that domain; opens a web page of yours if the app is not installed. Test it on a real device, because these links don't always open the app when they are reached through a redirect rather than a tap |
Register the link yourself in the portal: under **Settings > API Settings**, open the environment's row and add it under **Whitelisted Redirect URLs**. Users with the Administrator or Developer role can do this. Sandbox and production have separate lists, so register the link in each environment you use. Custom schemes are accepted in both environments; production rejects `http://` and `localhost` links, which sandbox allows. Banks do not need to whitelist your return link, and you don't need to contact support for it. Support is needed only for [App-to-App](/docs/shared/flows/app-to-app) returns, where the bank's app opens your app directly.
The `redirect_uri` you send must match a registered link, otherwise the request fails with 400 (`BP214` for Blink Payments, `BDT211` for Blink Data). Query strings are ignored, so you can add your own, such as `?order=123`. See [Redirect URI Whitelisting](/docs/shared/authentication#redirect-uri-whitelisting) for the matching rules.
## Handling the Outcome on Mobile
Your backend must know which consent or quick payment belongs to which order before the customer leaves your app, so store the `quick_payment_id` or consent ID on your backend as soon as you create it and before the app opens the browser. Keeping it only in the app is not enough: the operating system can close your app while the customer is in their bank's app.
When your return link arrives, check that it is your scheme, host and path, and that its `cid` matches the ID you stored for the order the customer is paying for; ignore a stale or unknown one. Branch on its `status` parameter, never on the `error` text, and treat the link as a hint only: it is not proof of payment, because it can be forged or never arrive. [Return Parameters](/docs/shared/flows/redirect-flow#return-parameters) lists what it can carry. Then ask your backend for the outcome, and have the backend GET the consent or quick payment from the BlinkPay API. Close the in-app browser at this point (see the [Platform Checklist](/docs/shared/flows/mobile-integration#platform-checklist)).
The return link often never arrives. Closing SFSafariViewController or a Custom Tab, pressing the back button or switching away from the app fires no redirect, and a customer who approves in their banking app may come back to your app by hand. So whenever your app returns to the foreground while a payment is in progress, ask your backend for the current status rather than waiting for the link.
Reconcile; don't assume failure. If the consent is still `AwaitingAuthorisation`, the customer may still approve in their banking app, so don't revoke it or treat it as failed just because they came back to your app. Poll it through your backend every second until it reaches a final status. A consent that is never authorised ends by itself when the window passes. For Blink Payments, once a payment exists, check it every second until it is `AcceptedSettlementCompleted` or `Rejected`. Stop polling at a final status; polling the same consent or payment hundreds of times does not make it finish sooner and can get your requests rate limited.
**Blink Payments only:** for a quick payment through the gateway or redirect flow, have your backend call `GET /payments/v1/quick-payments/{id}` as soon as the return link arrives or the app resumes; the gateway normally does this for you, and calling it again is safe. For a bank payment that call completes the payment. Run a backend sweep over stored quick payments that never got a return, and read each outcome from `consent.payments[0].status`.
While the outcome is not final, tell the customer that you are checking with their bank rather than showing a failure, and don't let them pay for the same order again until the first attempt has a final status, otherwise they may pay twice. If you stop waiting before it is final, show the order as awaiting confirmation, give the customer a way to check again later, and keep checking on your backend.
## Platform Checklist
- **iOS: dismiss the in-app browser.** SFSafariViewController stays on screen when your return link opens the app. Dismiss it when the link arrives (the Flutter Demo does this in `AppDelegate.swift`); the auth-session modes close themselves.
- **iOS: register the link.** A custom scheme goes in `CFBundleURLTypes` in `Info.plist`; a universal link needs the Associated Domains entitlement and an `apple-app-site-association` file on your domain.
- **Android: register the link.** Add an intent filter with `VIEW`, `DEFAULT` and `BROWSABLE` and your scheme and host to the activity that handles the return, or an `autoVerify` HTTPS filter for an App Link, with the matching `assetlinks.json` file on your domain.
- **Android: launch mode.** The return link must reach the activity that is already running, not start a second copy of it. Keep your framework's default (`singleTop` for Flutter, `singleTask` for React Native) and avoid `singleInstance`.
- **Android: package visibility.** From Android 11, add a `` entry for `VIEW` intents with the `https` scheme so your app can find a browser to open the Custom Tab. Add bank apps' package names only if your app checks whether a bank app is installed.
- **Cold start.** Handle the return link both when the app is running and when it is launched by the link, because the operating system may have closed it while the customer was in their bank's app.
- **Emulators and simulators.** The Android emulator reaches your development machine at `10.0.2.2`, not `localhost`; the iOS Simulator can use `localhost`; a physical device needs your machine's network address or a tunnel. This applies to your backend URL; the return link is your app's own link and needs no network address. Bank apps are not available on emulators, but the sandbox test bank (PNZ) uses the browser, so the whole sandbox flow can be tested there.
- **Device testing.** Install the NZ banks' apps on a real device to test linking out to the bank's app. In production, installing a bank's app lets you check that the link opens the right app even without an account at that bank, though you can't complete a payment there. Also test closing the in-app browser, pressing back, switching apps and having the app closed mid-payment, and check that each ends with the right status shown.
# App-to-App Flow (https://merchants.blinkpay.co.nz/docs/shared/flows/app-to-app.md)
---
title: App-to-App Flow
description: Return customers straight from their banking app to your mobile app with redirect_to_app
---
# App-to-App Flow
**Blink Payments only:** this page applies to the Payment API.
App-to-app return sends the customer straight from their banking app back to your mobile app once they have approved, without passing through a browser on the way back. It builds on the standard [Mobile Integration](/docs/shared/flows/mobile-integration): your app opens the gateway or the bank's page in an in-app browser (SFSafariViewController or Chrome Custom Tabs), and the bank's app does the rest.
**Enabled by BlinkPay Support**
App-to-app return must be enabled for your account, for each bank, by BlinkPay support. Contact [support@blinkpay.co.nz](mailto:support@blinkpay.co.nz) to request it.
## Two Kinds of Deep Link
"Deep link" is used for two different things on mobile, and only the second needs this page.
| | Opening the bank's app | Returning to your app (app-to-app) |
| --- | --- | --- |
| What happens | The bank's authorisation page opens in the customer's banking app instead of the browser | After approving, the banking app opens your app directly |
| What you set up | Nothing: it depends on the bank | Send `redirect_to_app: true` when you create the consent, once BlinkPay support has enabled it for your account and the bank |
| Banks | Depends on the bank; see [Bank Coverage](/docs/shared/bank-coverage#mobile-app-deep-linking) | ASB and Westpac; see [Bank Coverage](/docs/shared/bank-coverage#mobile-app-deep-linking) |
`redirect_to_app: true` asks the bank to return the customer straight to your app. Without it, or if the bank doesn't support it or it isn't enabled for your account, the customer first lands on a BlinkPay page in their phone's browser after approving in their banking app. That page then opens your app through your return link, or, if it can't, asks the customer to return to your app themselves. Either way, the in-app browser you opened is still on screen behind the bank's app.
## Supported Banks
ASB and Westpac offer app-to-app return; ANZ, BNZ, Kiwibank and NZHL do not. The per-bank table is in [Bank Coverage](/docs/shared/bank-coverage#mobile-app-deep-linking). For a bank that doesn't offer it, or that has not been enabled for your account, `redirect_to_app` is silently ignored and the customer returns through your normal return link.
## How It Works
App-to-app works with the redirect flow, and with the gateway flow when the customer takes the redirect option. It is not available with the decoupled flow. It works in sandbox and production once enabled for that environment.
1. Your backend creates the consent or quick payment with `redirect_to_app: true` and your app's return link as the `redirect_uri`, and stores the returned ID against the order.
2. Your app opens the returned `redirect_uri` in the in-app browser. On mobile the bank's page opens the customer's banking app.
3. The customer approves in their banking app.
4. The banking app opens your app's return link with the bank's `code` and `state` parameters (or an `error`), not with `cid`.
5. Your app, or your backend on its behalf, passes every query parameter it received, unchanged, to BlinkPay's return endpoint with `redirect=false` added. On success BlinkPay answers `204 No Content`. On a failure it answers with a `302` whose `Location` carries `cid`, `status` and `error`, so handle both.
6. Your app closes the in-app browser and asks your backend for the outcome, and your backend GETs the consent or quick payment by the ID it stored. For a quick payment, retrieving it completes the payment, so make this GET straight away (see [Handling the Outcome on Mobile](/docs/shared/flows/mobile-integration#handling-the-outcome-on-mobile)).
```http
GET /bank/1.0/return?state={state}&code={code}&redirect=false HTTP/1.1
Host: debit.blinkpay.co.nz
```
In sandbox, use `sandbox.debit.blinkpay.co.nz`, the host you created the consent on.
Because the option is silently ignored for a bank that is not enabled, the same return link can also receive a normal return carrying `cid` (and `status` on failure). Handle both: forward `code` and `state` when they are present, and otherwise treat it as a normal return. Either way, the outcome comes from your backend's GET, never from the link itself.
## Getting Started
1. Contact [support@blinkpay.co.nz](mailto:support@blinkpay.co.nz) to request app-to-app return, and say which banks and environments you need.
2. Provide your app's return link for sandbox and for production.
3. Register the same links under **Settings > API Settings** in the Whitelisted Redirect URLs for each environment (see [Register Your Return Link](/docs/shared/flows/mobile-integration#register-your-return-link)).
BlinkPay registers your return link with each participating bank. Approval is at each bank's discretion and timelines vary, so build and test with the standard return (through the browser) in the meantime.
**Plan Your URLs Carefully**
Return links are difficult to change once registered with banks. Make sure your scheme, host and path are final before you request app-to-app return.
## Implementation
Add `redirect_to_app: true` to the flow details when you create the consent:
```http
POST /payments/v1/single-consents HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
Content-Type: application/json
{
"flow": {
"detail": {
"type": "redirect",
"bank": "ASB",
"redirect_uri": "https://app.example.com/blinkpay/return",
"redirect_to_app": true
}
},
"amount": {
"currency": "NZD",
"total": "50.00"
},
"pcr": {
"particulars": "Order",
"code": "12345",
"reference": "Ref"
}
}
```
With the gateway flow, set `redirect_to_app: true` in the gateway flow details; it applies when the customer takes the redirect option.
Configure your app so that the return link reaches the activity or scene that is already running: on Android, the launch mode advice in the [Platform Checklist](/docs/shared/flows/mobile-integration#platform-checklist) applies, and on iOS, dismiss the in-app browser when the link arrives.
## Error Handling
### Deep Link Failures
If the device cannot open your app from the return link, for example because the app is not installed, a universal link or App Link opens its web page instead. Create a page at that URL explaining that the app couldn't be opened and that the payment may not have completed. The customer's consent is still there to be checked: your backend can GET it by the ID it stored.
# Data Retrieval (https://merchants.blinkpay.co.nz/docs/data/guides/data-retrieval.md)
---
title: Data Retrieval
description: Guide to retrieving account information through Blink Data APIs
---
# Data Retrieval
This guide covers retrieving account data through Blink Data APIs.
**Consent Required**
Before retrieving data, you must create and authorise a consent. See the [Quick Start](/docs/data/quick-start) guide for the complete flow.
Each data endpoint needs a permission on the consent, which the customer approved when they authorised it. See [Permissions](/docs/data/concepts/permissions) for the full mapping.
## Bank Metadata
Before retrieving data, use the metadata endpoint to discover which features each bank supports.
**Request:**
```http
GET /v1/meta HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Response:**
```json
[
{
"bank_id": "0b7a9c3e-5d2f-4e8a-9f61-2c4d8e7b1a05",
"name": "PNZ",
"features": {
"accounts": true,
"balances": true,
"transactions": true,
"statements": true,
"party": true
}
}
]
```
The response is a top-level array with one entry per bank enabled for your organisation. `bank_id` differs between sandbox and production, so match on `name` rather than hard-coding the ID.
**Why use metadata:**
- Banks continuously add new features
- Your application can adapt automatically
- No code changes needed when features become available
**Best practices:**
- Cache metadata response for 24 hours
- Check feature availability before creating consents
- Provide fallback workflows for unsupported features
## Accounts API
### List Accounts
**Request:**
```http
GET /v1/accounts?consent_id={consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Required Permission:** `ReadAccounts`
The customer chooses which accounts to share at their bank, so the list can hold more than one account. It accepts `page` and `size` (see [Pagination](/docs/data/guides/data-retrieval#pagination)).
**Response:**
```json
{
"accounts": [
{
"account_id": "eyJhY2NvdW50SWQiOiIxMjM0NTY3ODkwIn0",
"name": "Everyday Account",
"type": "current",
"currency": "NZD",
"details": {
"number": "12-3456-7890123-00",
"name": "Everyday Transactional Account"
}
},
{
"account_id": "eyJhY2NvdW50SWQiOiIwOTg3NjU0MzIxIn0",
"name": "Savings Account",
"type": "savings",
"currency": "NZD",
"details": {
"number": "12-3456-7890123-01",
"name": "Savings Account"
}
}
],
"pagination": {
"page": 1,
"size": 500,
"total_pages": 1,
"total_records": 2
}
}
```
### Get an Account
```http
GET /v1/accounts/{account_id}?consent_id={consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Required Permission:** `ReadAccounts`
Returns a single account object, in the same shape as an entry in the `accounts` list above.
## Balances API
### Get Account Balances
**Request:**
```http
GET /v1/accounts/{account_id}/balances?consent_id={consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Required Permission:** `ReadBalances`
**Response:**
```json
{
"balances": [
{
"account_id": "eyJhY2NvdW50SWQiOiIxMjM0NTY3ODkwIn0",
"type": "current",
"amount": {
"amount": "1234.56000",
"currency": "NZD"
},
"direction": "in",
"date_time": "2026-10-07T10:30:00+13:00"
},
{
"account_id": "eyJhY2NvdW50SWQiOiIxMjM0NTY3ODkwIn0",
"type": "available",
"amount": {
"amount": "1734.56000",
"currency": "NZD"
},
"direction": "in",
"date_time": "2026-10-07T10:30:00+13:00"
}
],
"pagination": {
"page": 1,
"size": 500,
"total_pages": 1,
"total_records": 2
}
}
```
### Balance Types
The endpoint returns two balance types, `current` and `available`.
| Type | Description |
| ----------- | --------------------------------------------------------------------------------------------------------------------- |
| `current` | Cleared funds balance |
| `available` | Booked funds plus pending items. Includes an overdraft or credit limit only where the bank reports one on the account |
`date_time` is the point in time the balance was calculated at. Some banks calculate balances once a day, in which case `date_time` reads as midnight of the current day rather than the time of your request.
### Direction
| Value | Description |
| ----- | ---------------------------------- |
| `in` | Positive balance (money available) |
| `out` | Negative balance (overdraft) |
## Transactions API
### Get Transactions
**Request:**
```http
GET /v1/accounts/{account_id}/transactions?consent_id={consent_id}&from_booking_date_time={date}&to_booking_date_time={date}&page={page}&size={size} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Required Permission:** `ReadTransactions`
**Parameters:**
| Parameter | Required | Description |
| ------------------------ | -------- | ------------------------------------------------- |
| `consent_id` | Yes | The consent ID |
| `from_booking_date_time` | Yes | Start of the range (ISO 8601 date-time, with offset) |
| `to_booking_date_time` | No | End of the range (ISO 8601 date-time, with offset) |
| `page` | No | Page number (default: 1) |
| `size` | No | Records per page (default: 500, max: 500) |
**Date filters:** send a full ISO 8601 date-time with an offset (`2026-09-01T00:00:00Z` or `2026-09-01T00:00:00+12:00`); a date on its own is rejected. The range must sit inside the consent's `start_date_time` to `end_date_time` window, the start can't be in the future and the end can't be after today. The same rules apply to the statement date filters. Timestamps in responses are in New Zealand time (`+12:00` or `+13:00`).
**Response:**
```json
{
"transactions": [
{
"transaction_id": "txn-123",
"type": "POS",
"direction": "out",
"amount": {
"amount": "45.50000",
"currency": "NZD"
},
"status": "posted",
"date_time": "2026-09-15T14:23:00+12:00",
"description": "Coffee Shop",
"balance": {
"account_id": "eyJhY2NvdW50SWQiOiIxMjM0NTY3ODkwIn0",
"type": "current",
"amount": {
"amount": "1189.06000",
"currency": "NZD"
},
"direction": "in",
"date_time": "2026-09-15T14:23:00+12:00"
},
"merchant": {
"name": "Coffee Shop",
"category": "5812"
}
},
{
"transaction_id": "txn-124",
"type": "DC",
"direction": "in",
"amount": {
"amount": "1500.00000",
"currency": "NZD"
},
"status": "posted",
"date_time": "2026-09-14T09:00:00+12:00",
"description": "Salary Payment",
"balance": {
"account_id": "eyJhY2NvdW50SWQiOiIxMjM0NTY3ODkwIn0",
"type": "current",
"amount": {
"amount": "2689.06000",
"currency": "NZD"
},
"direction": "in",
"date_time": "2026-09-14T09:00:00+12:00"
},
"reference": {
"from_party": {
"name": "ACME Corp",
"reference": {
"reference": "Payroll"
}
},
"to_party": {
"name": "J Smith",
"reference": {
"particulars": "Salary",
"code": "Sep",
"reference": "John Smith"
}
}
}
}
],
"pagination": {
"page": 1,
"size": 500,
"total_pages": 3,
"total_records": 1234
}
}
```
### Reading Transactions
`amount.amount` is a string that is never signed (up to 13 digits and 5 decimal places). The sign is carried by `direction`: `in` is money into the account and `out` is money out of it.
`transaction_id` is optional. Some banks don't send it, so don't use it as a unique key without a fallback. `type` is the bank's own transaction code, passed through unchanged; its format differs by bank (the values above are illustrative), so don't map on it across banks. `merchant` (name and MCC `category`), `card`, `from_account`, `to_account` and `reference` are optional; handle them being absent.
In `reference`, the parties are fixed by role, not by `direction`: `from_party` is the payer and `to_party` is the payee, each with the name and the particulars, code and reference on their side of the payment. For an incoming payment such as the salary above, the payer's name is in `from_party.name` and the details the payer entered for the account holder are usually in `to_party.reference`. For an outgoing payment, the account holder is the payer.
### Transaction Status
| Status | Description |
| --------- | --------------------------------- |
| `posted` | Transaction completed and cleared |
| `pending` | Transaction pending/processing |
## Statements API
Statements are available at some banks only: see [Bank Coverage](/docs/shared/bank-coverage), or check `features.statements` in `GET /v1/meta`. The PNZ test bank supports them. All three statement endpoints need the `ReadStatements` permission on the consent.
### List Statements
**Request:**
```http
GET /v1/accounts/{account_id}/statements?consent_id={consent_id}&from_statement_date_time=2026-01-01T00:00:00Z HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Required Permission:** `ReadStatements`
**Parameters:**
| Parameter | Required | Description |
| -------------------------- | -------- | ---------------------------------------------------- |
| `consent_id` | Yes | The consent ID |
| `from_statement_date_time` | Yes | Start of the range (ISO 8601 date-time, with offset) |
| `to_statement_date_time` | No | End of the range (ISO 8601 date-time, with offset) |
| `page` | No | Page number (default: 1) |
| `size` | No | Records per page (default: 500, max: 500) |
The date filters follow the same rules as [transactions](/docs/data/guides/data-retrieval#get-transactions).
**Response:**
```json
{
"statements": [
{
"statement_id": "stmt-001",
"date": "2026-09-01T00:00:00+12:00",
"type": "regular",
"description": "August 2026 Statement",
"period": {
"from": "2026-08-01T00:00:00+12:00",
"to": "2026-08-31T23:59:59+12:00"
},
"starting_balance": {
"amount": {
"amount": "2104.10000",
"currency": "NZD"
},
"direction": "credit"
},
"closing_balance": {
"amount": {
"amount": "1689.06000",
"currency": "NZD"
},
"direction": "credit"
}
}
],
"pagination": {
"page": 1,
"size": 500,
"total_pages": 1,
"total_records": 1
}
}
```
`type` is `regular` for a periodic statement or `interim` for one issued part-way through a period. `starting_balance` and `closing_balance` are optional; their `direction` is `credit` or `debit`.
### Get a Statement
```http
GET /v1/accounts/{account_id}/statements/{statement_id}?consent_id={consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Required Permission:** `ReadStatements`
Returns a single statement object, in the same shape as an entry in the `statements` list above.
### Download Statement
**Request:**
```http
GET /v1/accounts/{account_id}/statements/{statement_id}/file?consent_id={consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Required Permission:** `ReadStatements`
Returns the statement file as a download (`Content-Disposition: attachment`). Production banks provide a PDF (`application/pdf`); the PNZ test bank returns plain text (`text/plain`) instead, so read the `Content-Type` header rather than assuming a PDF.
## Party API
### Get Party Information
Two endpoints return a single party object. `GET /v1/party` returns the customer who authorised the consent; `GET /v1/accounts/{account_id}/party` returns the party for a specific account.
**Request:**
```http
GET /v1/party?consent_id={consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
```http
GET /v1/accounts/{account_id}/party?consent_id={consent_id} HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Required Permission:** `ReadParty`
**Response:**
```json
{
"party_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "John Smith",
"party_type": "personal",
"email_address": "john@example.com",
"phone": "+6421234567",
"address": {
"street_address": "123 Main Street",
"suburb": "Central",
"city": "Auckland",
"post_code": "1010",
"country": "NZ"
}
}
```
`party_type` is `personal` (sole or joint holder) or `delegate`. `email_address`, `phone` and `address` are optional and depend on what the bank holds.
## Pagination
Accounts, balances, transactions and statements are paginated.
| Parameter | Default | Max | Description |
| --------- | ------- | --- | ---------------- |
| `page` | 1 | - | Page number |
| `size` | 500 | 500 | Records per page |
The response's `pagination` object holds `page`, `size`, `total_pages` and `total_records`.
**Use Returned Values**
Every page holds `size` records except the last. Stop at `total_pages`; a page past the end returns an empty list. If the result is too large to return (400 `BDT263`), narrow the date range.
# Testing Guide (https://merchants.blinkpay.co.nz/docs/data/testing.md)
---
title: Testing Guide
description: Sandbox testing and test scenarios for Blink Data integration
---
# Testing Guide
Testing your integration in the sandbox environment is essential before going live with Blink Data.
## Sandbox Environment
**Sandbox URL:** `https://sandbox.data.blinkpay.co.nz`
The sandbox environment provides:
- Full API functionality with mock bank (PNZ)
- Simulated data access flows
- All consent flows supported (Gateway, Redirect, Decoupled)
**Limitations:**
- Mock data only (not real customer data)
- PNZ is the only bank (see [Sandbox and Production Banks](/docs/data/testing#sandbox-and-production-banks))
- Some bank-specific behaviours simplified
### Getting Sandbox Credentials
Sandbox credentials are provisioned when you sign up at [merchants.blinkpay.co.nz](https://merchants.blinkpay.co.nz/). If **Settings > API Settings** shows no sandbox key, select **Request Sandbox API Key** there. See [Getting API Credentials](/docs/shared/authentication#getting-api-credentials).
## Sandbox and Production Banks
In the sandbox, test every flow with **PNZ**, the test bank; it supports every data type, including statements. The production banks (ANZ, ASB, BNZ, Westpac, Kiwibank and NZHL) are used in production, with their `bank` values, decoupled identifiers and statement availability listed on the [Bank Coverage](/docs/shared/bank-coverage) page. `GET /v1/meta` lists the banks enabled for your organisation in the environment you are calling. The `bank` value is case-sensitive; an unrecognised value is rejected with 400 (see [Error Codes](/docs/data/error-codes#_400-bad-request)).
## Try the Demo
To see the Data API in action before building your integration, visit our demo site:
**ACME Loans Demo:** [https://acme-loans-dev.blinkpay.co.nz](https://acme-loans-dev.blinkpay.co.nz)
This demonstrates the full data consent and retrieval experience.
## Sandbox Test Values
The sandbox has one test bank, PNZ: Payments NZ's API Centre test bank, and the only bank shown in the Gateway. Use these values for every flow:
| Item | Value |
| --- | --- |
| Test bank | **PNZ** (the only bank in the sandbox Gateway) |
| Redirect or Gateway login | Username `user01`, password `password` |
| Decoupled, auto-approve | Mobile number `+64-259531933`. In the Gateway, choose **Send to PNZ app**. The consent is approved automatically. |
| Decoupled, any other number | Accepted, but the consent never authorises: it stays `AwaitingAuthorisation` until the bank's authorisation window ends, then becomes `Rejected` (timeout). Other numbers do not simulate a decline. |
| Decline | On the PNZ login page, choose **Cancel** before logging in. The consent becomes `Rejected`. |
**Automated Testing**
The auto-approve number needs no customer interaction, which makes it the easiest way to drive consents to `Authorised` in automated tests.
## Test Scenarios
### 1. Happy Path - Redirect Flow
**Objective:** Verify end-to-end data access using Redirect Flow
**Steps:**
1. Create consent with redirect flow (PNZ bank)
2. Redirect user to bank URL
3. Complete authentication with `user01` / `password`
4. Handle redirect back to your application
5. Verify consent status is `Authorised`
6. Retrieve accounts list
7. Retrieve balances for each account
8. Retrieve transactions for an account
**Expected Results:**
- Consent status: `Authorised`
- Account data returned successfully
- Redirect back to your application
### 2. Happy Path - Decoupled Flow
**Objective:** Verify data access using Decoupled Flow
**Steps:**
1. Create consent with decoupled flow (PNZ bank, `+64-259531933`)
2. Poll the consent status every 5 seconds
3. Verify consent status is `Authorised`
4. Retrieve account data
**Expected Results:**
- The consent is authorised automatically; only `+64-259531933` does this, and any other number stays `AwaitingAuthorisation` until the window ends
- Account data retrieved successfully
### 3. Error Handling - Timeout
**Objective:** Verify timeout handling
**Steps:**
1. Create consent with redirect flow
2. Do not complete the bank authorisation
3. Wait out the bank's authorisation window (up to 10 minutes covers every bank)
4. Retrieve the consent and verify its status has changed to `Rejected`
5. Handle the timeout gracefully in your application
**Expected Results:**
- Consent status: `Rejected`
- Application shows appropriate error message
### 4. Error Handling - Rejection
**Objective:** Verify rejection handling
**Steps:**
1. Create consent with redirect flow
2. Redirect to bank
3. Choose **Cancel** on the PNZ login page, before logging in
4. Handle redirect back with rejection status
5. Verify consent status is `Rejected`
**Expected Results:**
- Consent status: `Rejected`
- Application handles rejection gracefully
### 5. Error Handling - Revocation
**Objective:** Verify revoked consent handling
**Steps:**
1. Create and authorise a consent (the auto-approve number is quickest)
2. Retrieve some account data successfully
3. Revoke the consent via `DELETE /v1/consents/{consent_id}` (204)
4. Attempt to retrieve account data again
5. Verify you receive 403 with code `BDT410`
6. Handle the revoked consent gracefully
**Expected Results:**
- Data retrieval fails with 403 `BDT410` after revocation, and revoking the consent again returns 409 `BDT702`
- Application detects revoked consent and prompts for new authorisation
### 6. Pagination Testing
**Objective:** Verify pagination implementation
**Steps:**
1. Create and authorise a consent
2. Retrieve transactions with `size=10`
3. Iterate through multiple pages using returned pagination values
4. Verify pagination metadata is correct
5. Test edge cases (last page, page beyond total)
**Expected Results:**
- All pages retrieved correctly
- Pagination metadata accurate
- Edge cases handled gracefully
## Producing Sandbox Errors
To test your error handling, you can deliberately produce each failure state:
| Error State | When It Occurs |
| ---------------- | ----------------------------------------------------------------------------------------------------------------- |
| `Rejected` | The customer chooses **Cancel** on the PNZ login page, or never completes authorisation before the bank's window elapses |
| `GatewayTimeout` | A Gateway Flow consent is abandoned before the customer selects a bank, and the 15-minute Gateway session cutoff elapses |
| `Revoked` | The consent is revoked after authorisation, via `DELETE /v1/consents/{consent_id}` |
For automated tests, drive each state yourself: authorise a consent with the auto-approve number and revoke it with `DELETE /v1/consents/{consent_id}` to exercise your `Revoked` handling, and choose **Cancel** on the PNZ login page for `Rejected`. Handle the remaining outcomes, such as expiry or a bank error, from the documented consent statuses and [error codes](/docs/data/error-codes).
## Next Steps
- **[Going Live](/docs/shared/help/going-live)** - Production checklist when testing is complete
# Security & Compliance (https://merchants.blinkpay.co.nz/docs/shared/security.md)
---
title: Security & Compliance
description: Learn about BlinkPay's security measures, certifications, and data protection practices
---
# Security & Compliance
Security is at the core of BlinkPay's design. We maintain bank-grade security standards to protect your business and your customers' financial data.
## ISO 27001:2022 Certification
BlinkPay is certified to ISO 27001:2022, the international standard for information security management systems. This certification demonstrates our commitment to:
- Systematic security management
- Regular risk assessments and audits
- Incident response and management
- Continuous improvement processes
## No Screen Scraping
Unlike some financial service providers, BlinkPay never uses screen scraping:
**Screen Scraping Risks**
Screen scraping requires storing customer banking credentials and mimicking user actions. This approach:
- Exposes credentials to third parties
- Creates security vulnerabilities
- Breaks when banks update their interfaces
- Violates many bank terms of service
**BlinkPay's Approach**
- Direct integration with official bank APIs
- No credential storage required
- Bank-to-bank secure communication
- Compliant with bank security requirements
## Encrypted Data
All data is encrypted both in transit and at rest:
- **In Transit**: All communications use HTTPS/TLS encryption
- **At Rest**: Stored data is encrypted using industry-standard encryption
## Two-Way Authentication
All BlinkPay integrations use two-way authentication:
**BlinkPay β Your Application:**
- OAuth 2.0 tokens for API access
- HTTPS with certificate validation
**BlinkPay β Banks:**
- Mutual TLS authentication
- Certificate-based authentication
**Customer β Bank:**
- Customers authenticate directly with their bank using their existing banking credentials
- BlinkPay never sees or stores banking credentials
## Consent Management
All access requires explicit customer consent:
- **Clear Purpose**: Customers see exactly what they're authorising
- **Scope Limitation**: Access limited to authorised accounts and actions
- **Revocable**: Customers can revoke consent at any time through their bank or your application
## Data Handling
BlinkPay operates as an integration layer between your application and banks:
**No Customer Data Storage**
BlinkPay does not hold any Customer Personally Identifiable Information for first-party use. We hold information solely on behalf of merchants and banks as part of the payment integration process.
## Testing & Security
**Prior Permission Required**
Prior permission must be obtained before conducting penetration tests or load tests. This applies to any environment hosted by BlinkPay.
# Bank Coverage (https://merchants.blinkpay.co.nz/docs/shared/bank-coverage.md)
---
title: Bank Coverage
description: Detailed coverage of supported banks, authentication flows, and bank-specific features
---
# Bank Coverage
BlinkPay connects to all major New Zealand banks, providing comprehensive coverage for retail and personal banking accounts.
## Supported Banks
Both products connect to the same six production banks. Blink Data can read accounts, balances, transactions and party details at every bank, but statements only at some:
| Bank | `bank` value | Blink Payments | Blink Data | Blink Data statements |
| ----------------------------- | ------------ | -------------- | ---------- | --------------------- |
| **ANZ New Zealand** | `ANZ` | β | β | β |
| **ASB Bank** | `ASB` | β | β | β |
| **Bank of New Zealand (BNZ)** | `BNZ` | β | β | β |
| **Kiwibank** | `Kiwibank` | β | β | β |
| **NZHL** | `NZHL` | β | β | β |
| **Westpac New Zealand** | `Westpac` | β | β | β |
The `bank` value is case-sensitive. In the sandbox, test with PNZ, BlinkPay's test bank, which supports every flow and, for Blink Data, every data type including statements. Which banks you can use is set for your organisation: on Blink Data, a `bank` value the API doesn't recognise returns 400 `BDT243`, and a bank your organisation isn't set up for returns 400 `BDT213`.
Each bank supports different authentication flows and features. Understanding these differences helps you provide the best experience for your customers.
## Authentication Flow Support
Different banks support different authentication flows. Here's what's available:
### Redirect Flow Support
| Redirect Method | ASB | BNZ | Westpac | ANZ | Kiwibank | NZHL |
| ------------------------------- | --------- | -------------- | -------- | --------- | -------- | -------- |
| **Browser Redirect (Web)** | β QR code | β Direct (2FA) | β Direct | β QR code | β Direct | β Direct |
| **Deep Link to Bank App (Mobile)** | β | β | β | β | β | β |
**Notes:**
- **ASB/ANZ Web:** Browser displays QR code. Customer scans with their banking app to approve.
- **BNZ:** Browser-based authentication only with 2FA required.
- **Deep Link to Bank App:** on a phone, the redirect deep links into the customer's banking app instead of opening the bank's website (ANZ needs a current version of goMoney). This is not the same as returning the customer to your app afterwards; see [Mobile App Deep Linking](#mobile-app-deep-linking).
### Decoupled Flow Support
| Identifier Type | ASB | BNZ | Westpac | ANZ | Kiwibank | NZHL |
| ----------------------------------------------- | --- | --- | ------- | --- | -------- | ---- |
| **Mobile Number** (`mobile_number`) | β | β | β | β | β | β |
| **Email** (`email`) | β | β | β | β | β | β |
| **Access Number** (`banking_username`) | β | β | β | β | β | β |
| **Consent ID** (`consent_id`, repeat customer) | β | β | β | β | β | β |
An identifier the bank doesn't recognise is not reported as an error: the consent is created and times out. ASB, BNZ and Westpac send a push notification for a decoupled request; ANZ sends a goMoney notification on iOS, and on Android the customer opens goMoney to find the request; Kiwibank customers open their app to find the request. See [Decoupled Flow Identifiers](#decoupled-flow-identifiers) for how to use each identifier.
### Gateway Flow
All banks support Gateway Flow on both products, and it is recommended for most integrations.
**Recommended: Gateway Flow**
The Gateway Flow is recommended for most integrations as it automatically selects the best available flow for each bank and handles all authentication complexity.
## Bank-Specific Features
### ANZ New Zealand
**Redirect Flow:**
- **Mobile:** Opens the ANZ goMoney app (the customer needs a current version)
- **Web/Desktop:** Redirects to ANZ web page displaying QR code. Customer scans with their ANZ app to approve.
**Decoupled Flow:**
- Mobile number or previous consent ID as the identifier
- Customer authorises on their mobile device
- goMoney notification on iOS; on Android, customers open goMoney to approve
**Payment Limits (Blink Payments only):** see [Payment Limits](#payment-limits). ANZ caps each payment rather than a daily total, and requires [Voice ID](https://www.anz.co.nz/banking-with-anz/ways-to-bank/guides/voice-id-phone-banking/) over $10,000.
**Blink Data:** accounts, balances, transactions, statements and party details.
**Session Timeout:**
- Set per bank and per flow - see [Timeouts and Deadlines](#timeouts-and-deadlines)
### ASB Bank
**Redirect Flow:**
- **Mobile:** Opens the ASB app
- **Web/Desktop:** Browser displays QR code. Customer scans with their ASB app to approve.
- Supports FastNet Classic and FastNet login
- Biometric authentication where enabled
**Decoupled Flow:**
- Mobile number or previous consent ID as the identifier
- Customer authorises on their mobile device
- Push notifications supported
**Payment Limits (Blink Payments only):** see [Payment Limits](#payment-limits).
**Blink Data:** accounts, balances, transactions, statements and party details.
**Session Timeout:**
- Set per bank and per flow - see [Timeouts and Deadlines](#timeouts-and-deadlines)
**Special Features:**
- Supports both FastNet Classic and new FastNet
- Wide range of account types supported
### Bank of New Zealand (BNZ)
**Redirect Flow:**
- Opens BNZ website for authorisation (browser-based only)
- 2FA required for authorisation
**Decoupled Flow:**
- Supported identifiers: Mobile Number, Email, or Consent ID
- Customer authorises on their mobile device
- Push notifications supported
**Payment Limits (Blink Payments only):** see [Payment Limits](#payment-limits).
**Blink Data:** accounts, balances, transactions, statements and party details. A consent's `start_date_time` can be no more than 2 years in the past.
**Session Timeout:**
- Set per bank and per flow - see [Timeouts and Deadlines](#timeouts-and-deadlines)
**2FA Requirements:**
**BNZ 2FA Restriction**
**Blink Payments:** devices registered with BNZ for less than 24 hours cannot make payments. This is a BNZ security requirement. Customers must wait 24 hours after registering a new device before completing payments.
### Kiwibank
**Redirect Flow:**
- Opens Kiwibank website for authorisation (browser-based only)
**Decoupled Flow:**
- Mobile number or previous consent ID as the identifier
- Push notifications not supported - customers open the app to find the request
**Payment Limits (Blink Payments only):** see [Payment Limits](#payment-limits).
**Blink Data:** accounts, balances, transactions and party details; statements are not available.
**Session Timeout:**
- Set per bank and per flow - see [Timeouts and Deadlines](#timeouts-and-deadlines)
### NZHL
**Redirect Flow:**
- Opens the NZHL website for authorisation on all devices
**Decoupled Flow:**
- Mobile number or previous consent ID as the identifier
**Payment Limits (Blink Payments only):** read the current limit from `payment_limit` on the Blink Payments metadata endpoint; see [Payment Limits](#payment-limits).
**Blink Data:** accounts, balances, transactions and party details; statements are not available.
**Session Timeout:**
- Set per bank and per flow - see [Timeouts and Deadlines](#timeouts-and-deadlines)
### Westpac New Zealand
**Redirect Flow:**
- Opens the Westpac app on mobile devices
- Supports Westpac One authentication
- App-to-app return to your app is available on request (Blink Payments only; see [Mobile App Deep Linking](#mobile-app-deep-linking))
**Decoupled Flow:**
- Supported identifiers: the customer's Access Number (sent as `banking_username`), or Consent ID
- Customer authorises on their mobile device
- Push notifications supported
**Payment Limits (Blink Payments only):** see [Payment Limits](#payment-limits).
**Blink Data:** accounts, balances, transactions, statements and party details.
**Session Timeout:**
- Set per bank and per flow - see [Timeouts and Deadlines](#timeouts-and-deadlines)
**Special Features:**
- Westpac One unified authentication
- Supports personal and business accounts (v2.3 limitations apply)
## Decoupled Flow Identifiers
The decoupled flow needs an identifier that tells the bank which customer to send the request to. The redirect flow needs none: the customer identifies themselves at the bank. Each bank accepts different identifier types:
| Bank | `identifier_type` values |
| ------------ | ------------------------------------------------------------- |
| **ANZ** | `mobile_number`, `consent_id` |
| **ASB** | `mobile_number`, `consent_id` |
| **BNZ** | `mobile_number`, `email`, `consent_id` |
| **Kiwibank** | `mobile_number`, `consent_id` |
| **NZHL** | `mobile_number`, `consent_id` |
| **Westpac** | `banking_username` (the customer's Access Number), `consent_id` |
Send the customer's mobile number as `mobile_number` (Westpac takes `banking_username` instead); use `mobile_number`, not `phone_number`. A type the bank doesn't accept is rejected when you create the consent, while a value the bank doesn't recognise is accepted and the consent simply times out.
**Using Previous Consents**
For a returning customer, send `consent_id` with the ID of an earlier consent your application created for that customer at the same bank. It must be `Authorised` or `Consumed`, not revoked, and the customer then enters nothing. If the bank rejects an old consent as an identifier, fall back to the customer's usual identifier.
## Payment Limits
Payment limits apply to Blink Payments only. Blink Data has no payment limits.
### Transaction Limits
**Blink Payments only.** Each bank limits how much a customer can pay, and the limits differ between one-off PayNow payments and AutoPay (enduring consent) payments:
| Bank | PayNow (one-off) | AutoPay (enduring) |
| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **ANZ** | $25,000 per payment, but no more than the customer's goMoney limit (default $10,000). No daily total. Voice ID needed over $10,000 | The same $25,000 and goMoney limits apply; the consent maximum can't exceed the goMoney limit. Voice ID needed if the consent maximum is over $10,000 |
| **BNZ** | $50,000 per day in total (shared with Internet Banking) | Same, no separate AutoPay limit |
| **Westpac** | $30,000 per day in total (shared with Westpac One) | Same, no separate AutoPay limit |
| **ASB** | $100,000 per day in total (shared with ASB Internet Banking and Mobile Banking) | $10,000 per payment, no daily limit (only limits set in the consent) |
| **Kiwibank** | $50,000 per day in total | Same; the daily limit covers PayNow and AutoPay together |
A daily total is the customer's own limit at that bank, so payments they have already made through the bank's other channels that day count towards it. ANZ's [Voice ID](https://www.anz.co.nz/banking-with-anz/ways-to-bank/guides/voice-id-phone-banking/) requirement is ANZ's, not a BlinkPay limit.
BlinkPay also applies a limit to your account; contact [support@blinkpay.co.nz](mailto:support@blinkpay.co.nz) to change it. Bank limits change, so where you need the current figure at runtime, read `payment_limit` from `GET /payments/v1/meta`, which returns one `{total, currency}` value per bank (including NZHL, which isn't in the table above), rather than hard-coding this table.
**Verify Large Payments**
For high-value payments, it's recommended to inform customers in advance, as some may need to increase their daily limit with their bank before authorising.
### Minimum Payment
The minimum payment amount across all banks is **$0.01 NZD**.
### Maximum Payment
The maximum payment amount, or how much a customer can pay, depends on their bank and on whether it is a PayNow or an AutoPay payment; see [Transaction Limits](#transaction-limits) for each bank's limit. Your account's own limit with BlinkPay also applies.
### Daily Limits
A customer's own daily limit can be lower than the bank maximum above, depending on their account type, security settings and history with the bank. Customers can typically view and adjust it through their internet banking or mobile app.
## Timeouts and Deadlines
Every timer that can end a consent or a payment, on both products: the gateway cutoff, each bank's authorisation timeout for the redirect and decoupled flows, the payment and card deadlines, and the token and idempotency lifetimes. Banks change their windows, so **Blink Payments** integrations should read the live authorisation windows from `GET /payments/v1/meta` (`redirect_flow.request_timeout` and `features.decoupled_flow.request_timeout`; see [Payment Metadata Fields](/docs/shared/reference/metadata#payment-metadata-fields)). `GET /v1/meta` on Blink Data has no timeouts.
| Timer | Product | Value | When it runs out |
| --------------------------------------- | -------------- | ------------------------------------------------------- | ------------------------------------------------------- |
| Gateway session, before a bank is chosen | Both | 15 minutes | Consent `GatewayTimeout` |
| Redirect authorisation window | Both | Per bank, see below | Consent `Rejected` |
| Decoupled authorisation window | Both | Per bank, see below | Consent `Rejected` |
| Bank quick payment retrieval | Blink Payments | Retrieve as soon as the customer returns | That retrieval completes the payment |
| Single consent, bank payment | Blink Payments | 48 hours after the consent becomes `Authorised` | Consent `Rejected` |
| Single consent, card reservation | Blink Payments | 5 days without a payment | Reservation released, consent `Revoked` |
| Card quick payment never captured | Blink Payments | 24 hours after authorisation | Payment `Rejected`, hold released |
| Card refund | Blink Payments | 60 days after the payment completes | Refund refused with `BP045`; contact support |
| Enduring consent | Blink Payments | `expiry_timestamp`, if you set one | Consent `Consumed` |
| Data consent | Blink Data | `expiration_date_time`, if you set one | Consent `Consumed` |
| Payment still `Pending` | Blink Payments | 6 hours | Contact support with the consent and payment IDs |
| Access token | Both | 1 hour | 401; request a new token |
| Idempotency key | Both | 24 hours | A reused key creates a new consent or payment |
### How Long the Customer Has to Authorise
The bank's authorisation window, from when the consent is created (or, in the gateway, from when they choose the bank). A consent that isn't authorised in time becomes `Rejected`.
| Bank | Redirect, Blink Payments | Redirect, Blink Data | Decoupled, Blink Payments | Decoupled, Blink Data |
| -------- | ------------------------ | -------------------- | ------------------------- | --------------------- |
| ANZ | 10 min | 10 min | 7 min | 7 min |
| ASB | 10 min | 10 min | 10 min | 5 min |
| BNZ | 10 min | 5 min | 4 min | 4 min |
| Westpac | 10 min | 10 min | 10 min | 10 min |
| Kiwibank | 10 min | 10 min | 5 min | 5 min |
| NZHL | 10 min | 10 min | 5 min | 5 min |
Show the customer a clear message when their session expires, and let them start again.
### Payment Authorisation
**Blink Payments only.** Once a consent is authorised, how long you have to take the payment depends on the consent type. Retrieve a bank quick payment as soon as the customer returns (see [Redirect Flow](/docs/shared/flows/redirect-flow)); that call completes the payment. A single consent must be paid within 48 hours of authorisation, otherwise it becomes `Rejected`. An enduring consent remains valid until its expiry date or until it is revoked.
**Blink Data:** an authorised consent stays valid until its `expiration_date_time` passes (it then becomes `Consumed`) or it is revoked, by you or by the customer at their bank. A consent created without `expiration_date_time` has no expiry; if the bank ends it, data calls return 403 `BDT415`.
## Account Type Support
### Supported Account Types
All banks support the following account types:
- **Savings Accounts** - Personal savings accounts
- **Everyday (Cheque) Accounts** - Transaction accounts
- **Credit Cards** - Credit card accounts (where applicable)
### Account Identification
Accounts are identified using:
- Account number and suffix
- Bank branch code
- Account name/label
### Multiple Accounts
Account selection happens at the bank, during authorisation. **Blink Payments:** the customer chooses the account to pay from, and with an enduring consent can change accounts for later payments. **Blink Data:** the customer chooses one or more accounts to share, so `GET /v1/accounts` can return several.
## Mobile App Deep Linking
"Deep linking" can mean two different things on mobile. The first is the bank's app opening when the customer is sent to the bank, instead of the bank's website; this needs nothing from you. The second is app-to-app return (`redirect_to_app`, Blink Payments only), which lets your app open directly from the bank's app once the customer has approved, instead of coming back through the browser. It needs custom setup with each bank, so request it through BlinkPay support, who can tell you more; the banks that offer it are below. See [App-to-App Flow](/docs/shared/flows/app-to-app) for how it works.
| Bank | Opens the Bank App on Mobile | App-to-App Return (`redirect_to_app`) |
| -------- | ---------------------------- | ------------------------------------- |
| ANZ | β (current goMoney app) | β |
| ASB | β | β On request |
| BNZ | β Website | β |
| Kiwibank | β Website | β |
| NZHL | β Website | β |
| Westpac | β | β On request |
Without app-to-app return, the customer comes back to your `redirect_uri`, which can be a custom scheme or universal link that opens your app. For complete mobile integration guidance, including in-app browsers (embedded WebViews are not supported), registering your return link, and platform requirements:
[View Mobile Integration Guide β](/docs/shared/flows/mobile-integration)
## Business Account Limitations
**Business Account Support**
To access a business account via open banking, it generally needs to be linked to a personal account. Current open banking standards (v2.3) are tailored for retail/personal banking, and business accounts with multi-authorisation requirements may have limitations.
### Future Support (Open Banking v3)
Version 3 of the open banking standard will address:
- Multi-authorisation requirements
- Enhanced business account support
- Corporate account types
- Improved delegation and permissions
## Supporting Customers by Bank
What to tell a customer who is having trouble authorising at their bank:
### ANZ
- Decoupled flow on Android: no notification arrives, so tell the customer to open goMoney and find the request (iOS customers get a notification)
- Blink Payments: a payment over the customer's goMoney limit, or over $10,000 without Voice ID, fails at ANZ; see [Payment Limits](#payment-limits)
### ASB
- Both FastNet types work
- Decoupled flow: the customer needs notifications enabled for the ASB app, or they should open the app to find the request
### BNZ
- Blink Payments: a device registered with BNZ less than 24 hours ago can't make payments. This is a BNZ security rule, so tell the customer to try again once the 24 hours have passed, or to use a device they registered earlier
- Redirect uses the BNZ website on every device (the BNZ app doesn't open)
- Decoupled flow: Mobile Number, Email or Consent ID
### Kiwibank
- Decoupled flow sends no notification: tell the customer to open the Kiwibank app and find the request
- Redirect uses the Kiwibank website on every device (the Kiwibank app doesn't open)
- Decoupled flow: Mobile Number or Consent ID
### Westpac
- Opens the Westpac app on mobile; app-to-app return available on request
- Decoupled flow: the customer's Access Number (`banking_username`) or Consent ID
## Troubleshooting
For common bank issues (authentication failures, payment limits, session timeouts) and their solutions:
[View Troubleshooting Guide β](/docs/shared/help/troubleshooting)
# Error Codes Reference (https://merchants.blinkpay.co.nz/docs/data/error-codes.md)
---
title: Error Codes Reference
description: Blink Data (BDT) error codes, with the cause of each and how to fix it
---
# Error Codes Reference
This page lists the error codes the Blink Data API returns, with the cause of each and what to do. A classified error carries a `code` (format `BDTnnn`) and a `message`; the message often names the rejected value, so log it.
Some errors carry no `code`. A missing required field or query parameter, or a date that can't be parsed, returns 400 with only a `message`, so read `message` and check the request against the [Data Retrieval guide](/docs/data/guides/data-retrieval). A `consent_id` that doesn't exist, or that belongs to another organisation, returns 404: treat it as not found.
## Error Response Format
```json
{
"timestamp": "2026-10-15T10:30:00.000+13:00",
"path": "/v1/accounts/0e2b6c35-8f0a-4d5e-9c1b-2a7d4f6e8b90/transactions",
"status": 400,
"error": "400 BAD_REQUEST",
"message": "To date time must not be before from date time",
"code": "BDT237"
}
```
Branch on `status` and `code`. Don't parse `error`.
## Common Error Codes
### 400 Bad Request
| Code | Cause | Fix |
| ------ | ----- | --- |
| BDT211 | The `redirect_uri` is not whitelisted for your organisation | Add it under **Settings > API Settings** in the portal, in the row for the environment you are calling |
| BDT212 | The `redirect_uri` is malformed | Send an absolute URI |
| BDT213 | The bank is valid but not set up for your organisation, for example a production bank in the sandbox | Use `PNZ` in the sandbox; in production, contact BlinkPay to enable the bank |
| BDT228 | The decoupled `identifier_type` is not supported by that bank | Use an identifier the bank accepts (see [Sandbox and Production Banks](/docs/data/testing#sandbox-and-production-banks)) |
| BDT230 | The mobile number is not valid | Check the number the customer entered |
| BDT240 | `expiration_date_time` is before `start_date_time` | Set the expiry after the start |
| BDT241 | A permission the bank doesn't offer, for example `ReadStatements` at a bank without statements | Remove the permission for that bank; `GET /v1/meta` lists each bank's features |
| BDT242 | `permissions` is empty | Send at least one permission |
| BDT243 | The `bank` value is not recognised | Use an exact, case-sensitive value: `ANZ`, `ASB`, `BNZ`, `Westpac`, `Kiwibank`, `NZHL` or `PNZ` |
| BDT244 | `expiration_date_time` is before `end_date_time` | Set the expiry on or after the end |
| BDT245 | Unknown `flow.detail.type` | Use `gateway`, `redirect` or `decoupled` |
| BDT246 | Unknown `flow.flow_hint.type` | Use `redirect` or `decoupled` |
| BDT251 | `end_date_time` is not after today (New Zealand date) | Use a date from tomorrow onwards, or omit it |
| BDT252 | `start_date_time` is in the future | Use a start date-time that is now or earlier |
| BDT254 | The `idempotency-key` header is longer than 255 characters | Use a shorter key; a UUID is recommended |
| BDT255 | Both `idempotency-key` and the deprecated `x-idempotency-key` were sent | Send only `idempotency-key` |
| BDT257 | `start_date_time` and `end_date_time` fall on the same date (New Zealand time) | Make the data window span at least two dates |
| BDT258 | `expiration_date_time` falls on the date the consent is created (New Zealand time) | Set an expiry on a later date, or omit it for no expiry |
| BDT259 | A field has the wrong JSON type | Check the field named in `message` |
| BDT260 | A `flow_hint` has no `bank` | Add `bank` to the hint; Blink Data requires it |
| BDT261 | `flow.detail.type` is missing or empty | Set `type` to `gateway`, `redirect` or `decoupled` |
| BDT262 | `flow.flow_hint.type` is missing | Add `type` to the hint |
| BDT264βBDT268 | The decoupled `callback_url` is malformed, uses a scheme other than `http` or `https` (production requires `https`), or its host is not public or does not resolve | Use a public URL, `https` in production |
| BDT236 | The requested dates fall outside the consent's `start_date_time` to `end_date_time` window | Keep the date range within the consent's window |
| BDT237 | The to date is before the from date | Swap or correct the dates |
| BDT238 | The to date is after today | Use today or earlier, or omit it |
| BDT239 | The from date is in the future | Use a from date today or earlier |
| BDT263 | The bank's result is too large to return in one request | Narrow the request, for example the date range, and retry |
| BDT299 | The request body could not be read | Check the JSON body against the examples in the [Data Retrieval guide](/docs/data/guides/data-retrieval) |
### 403 Forbidden
| Code | Cause | Fix |
| ------ | ----- | --- |
| BDT401 | The redirect flow is not enabled for that bank | Use another flow (the gateway or decoupled) or contact BlinkPay |
| BDT402 | The decoupled flow is not enabled for that bank | Use another flow (the gateway or redirect) or contact BlinkPay |
| BDT403 | The gateway flow is not enabled for your organisation | Use the redirect or decoupled flow, or contact BlinkPay to enable the gateway |
| BDT409 | The consent was rejected | Create a new consent |
| BDT410 | The consent has been revoked | Create a new consent |
| BDT411 | The consent doesn't include the permission this endpoint needs | Create a new consent with the required permission |
| BDT412 | The consent has expired (its `expiration_date_time` has passed; it becomes `Consumed`) | Create a new consent |
| BDT413 | The consent is not yet authorised | Wait for the customer to finish authorising; check with `GET /v1/consents/{consent_id}` |
| BDT415 | The bank says the consent is no longer usable, for example because the customer revoked it at the bank | Create a new consent |
### 404 Not Found
| Code | Cause | Fix |
| ------ | ----- | --- |
| BDT101 | The consent doesn't exist, or belongs to another organisation | Check the consent ID, and that you are calling the environment the consent was created in |
### 409 Conflict
| Code | Cause | Fix |
| ------ | ----- | --- |
| BDT701 | The idempotency key was reused with a different request body | Use a new idempotency key for a different request |
| BDT702 | The consent has already been revoked | No action needed; access is already removed |
| BDT704 | A consent request with the same idempotency key is still in progress | Retry with the same key once the first request completes |
### 422 Unprocessable Entity
| Code | Cause | Fix |
| ------ | ----- | --- |
| BDT009 | Revoking a consent that has already ended (`Rejected`, `GatewayTimeout` or `Consumed`) | No action needed; create a new consent if you still need access |
| BDT013 | The consent timed out at the gateway (`GatewayTimeout`) | Create a new consent |
| BDT014 | A BNZ consent with `start_date_time` more than 2 years ago | Use a start date within the last 2 years |
### 502 Bad Gateway
| Code | Cause | Fix |
| ------ | ----- | --- |
| BDT603 | The bank could not accept the request at this time | Wait, then retry with backoff |
### 503 Service Unavailable
| Code | Cause | Fix |
| ------ | ----- | --- |
| BDT601 | The bank's service is down or not responding | Retry with backoff; check for bank maintenance |
| BDT602 | The bank rate-limited the request | Wait briefly, then retry with backoff |
| BDT604 | A temporary Blink Data problem | Retry shortly |
## Need Help?
For errors not listed here or for assistance troubleshooting:
1. Check the `message` field for specific details
2. Review the [Troubleshooting Guide](/docs/shared/help/troubleshooting)
3. Contact [support@blinkpay.co.nz](mailto:support@blinkpay.co.nz)
# API Policies (https://merchants.blinkpay.co.nz/docs/shared/reference/api-policies.md)
---
title: API Policies
description: Acceptable use, security testing, and API change policies
---
# API Policies
## Acceptable Use of Services
Use BlinkPay services only in accordance with their intended use, and additionally in accordance with the [BlinkPay Acceptable Use Policy](https://www.blinkpay.co.nz/aup).
## Security and Load Testing
Prior permission must be obtained before conducting penetration tests or load tests against any BlinkPay environment. Contact [support@blinkpay.co.nz](mailto:support@blinkpay.co.nz) to arrange testing windows.
## API Rate Limits
The Blink Payments and Blink Data APIs limit how many requests each IP address can send in a short time. The limits allow for normal e-commerce traffic, including the polling intervals recommended on these pages.
When you go over a limit, the API responds `429 Too Many Requests` with a `Retry-After` header giving the number of seconds to wait. Wait at least that long before retrying, and if you are limited again, back off exponentially with some random jitter. Rely on the status and the header, not on the body.
Most rate limiting comes from avoidable traffic:
- **Reuse access tokens.** Request a new token only when the current one is about to expire; see [Token Caching](/docs/shared/authentication#token-caching).
- **Poll sensibly.** Don't poll while the customer is at their bank: use their return to your redirect URI, or a decoupled `callback_url`, to know when to look, then poll every second until the consent leaves `AwaitingAuthorisation`. In the direct decoupled flow with no callback, poll every 5 seconds. **Blink Payments:** poll a payment every second until it is `AcceptedSettlementCompleted` or `Rejected`, for a minute or two at most.
- **Stop at a terminal state**, and pick up anything still in flight with a periodic sweep rather than continuous polling.
If you expect high volumes, for example a sale or a large billing run, email [support@blinkpay.co.nz](mailto:support@blinkpay.co.nz) in advance with your expected peak transactions per hour. Security testing and load testing both need prior permission, as above.
## API Change Policy
To preserve backward compatibility, breaking changes such as endpoint naming updates or the removal of JSON elements will not occur until a major version (URL) change.
In general, additive changes may be made to request query parameters and JSON responses.
This policy applies only after your Go-Live Date.
### Non-Breaking Changes
These may occur at any time without advance notice:
- Addition of new endpoints
- Addition of new optional request parameters
- Addition of new fields in JSON responses
- Addition of new error codes
- Addition of new enum values
- Performance improvements
- Bug fixes
**Your Responsibility:**
- Design clients to ignore unknown response fields
- Don't rely on field order in JSON responses
- Handle new enum values gracefully
## Request Headers
### Optional Headers
The following optional headers can be included in API requests:
| Header | Description |
| ----------------------- | ---------------------------------------------------------- |
| `x-customer-ip` | The customer's IP address if they are currently logged in |
| `x-customer-user-agent` | The User-Agent of the application on the customer's device |
These headers help banks with fraud detection and compliance. Include them when available.
## Terms of Service
These API policies supplement BlinkPay's:
- [Terms of Service](https://merchants.blinkpay.co.nz/terms/v3-0.html)
- [Privacy Policy](https://www.blinkpay.co.nz/privacy)
- [Acceptable Use Policy](https://www.blinkpay.co.nz/aup)
In case of conflict, the Terms of Service take precedence.
## Questions?
For questions about these policies:
- Email: [support@blinkpay.co.nz](mailto:support@blinkpay.co.nz)
- Documentation: [merchants.blinkpay.co.nz](https://merchants.blinkpay.co.nz/)
# Metadata Endpoint (https://merchants.blinkpay.co.nz/docs/shared/reference/metadata.md)
---
title: Metadata Endpoint
description: Query bank features, limits, and capabilities
---
# Metadata Endpoint
Each product has its own metadata endpoint, returning information about the banks available to you and their capabilities. Use it to configure your integration from bank features rather than hard-coding them. The two responses differ: only the Blink Payments one carries payment limits, flow support and authorisation timeouts.
## Payment API (Blink Payments)
```http
GET /payments/v1/meta HTTP/1.1
Host: sandbox.debit.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Required scope:** `view:metadata`
### Response
```json
[
{
"name": "ASB",
"payment_limit": {
"total": "100000",
"currency": "NZD"
},
"features": {
"enduring_consent": {
"enabled": true,
"consent_indefinite": false
},
"decoupled_flow": {
"enabled": true,
"available_identifiers": [
{ "type": "mobile_number", "name": "Mobile Number" },
{ "type": "consent_id", "name": "Consent ID" }
],
"request_timeout": "PT10M"
}
},
"redirect_flow": {
"enabled": true,
"request_timeout": "PT10M"
}
}
]
```
### Payment Metadata Fields
| Field | Description |
| ----------------------------------------------- | ---------------------------------------- |
| `payment_limit.total` | BlinkPay's maximum for a single payment to this bank, in NZD: one value per bank, applied to every flow and every consent type. An amount string such as `"100000"`. A payment over it is rejected with `400` BP229. It doesn't show the bank's own limits, such as a daily total or ASB's $10,000 per AutoPay payment, so a smaller payment can still be declined at the bank; see [Transaction Limits](/docs/shared/bank-coverage#transaction-limits). A separate limit applies to your account |
| `features.decoupled_flow.enabled` | Whether decoupled flow is supported |
| `features.decoupled_flow.available_identifiers` | Supported identifier types |
| `features.decoupled_flow.request_timeout` | Timeout for decoupled consent (ISO 8601) |
| `features.enduring_consent.enabled` | Whether recurring payments are supported |
| `features.enduring_consent.consent_indefinite` | Whether indefinite consents are allowed |
| `features.enduring_consent.maximum_consent` | Maximum consent duration (ISO 8601) |
| `features.card_payment.enabled` | Whether card payments are supported |
| `features.card_payment.allowed_card_payment_types` | Supported card payment types (e.g. `PANENTRY`, `GOOGLEPAY`) |
| `features.card_payment.allowed_card_networks` | Supported card networks (e.g. `VISA`, `MASTERCARD`) |
| `redirect_flow.enabled` | Whether redirect flow is supported |
| `redirect_flow.request_timeout` | Timeout for redirect consent (ISO 8601) |
### Card Payment Metadata
When card payments are enabled for your account, an entry with `"name": "Card"` appears in the same array as the bank entries:
```json
{
"name": "Card",
"features": {
"card_payment": {
"enabled": true,
"allowed_card_payment_types": [
"APPLEPAY",
"CLICKTOPAY",
"GOOGLEPAY",
"PANENTRY"
],
"allowed_card_networks": [
"VISA",
"MASTERCARD"
]
}
}
}
```
If the card payment entry is not present, card payment onboarding has not been completed; the Blink Payments card payments guide describes the onboarding steps.
## Data API (Blink Data)
```http
GET /v1/meta HTTP/1.1
Host: sandbox.data.blinkpay.co.nz
Authorization: Bearer {access_token}
```
**Required scope:** `read:meta`
### Response
The response is a top-level JSON array with one entry per bank enabled for your organisation, so it lists only the banks you can create consents for:
```json
[
{
"bank_id": "59a68dd9-6070-11f0-bb8d-0ad2a4bc413d",
"name": "ASB",
"features": {
"accounts": true,
"balances": true,
"transactions": true,
"statements": true,
"party": true
}
}
]
```
### Data Metadata Fields
| Field | Description |
| ----------------------- | ------------------------------------------------ |
| `bank_id` | Identifier for the bank. It differs between sandbox and production, so don't hard-code it; use `name` (the `bank` value you send when creating a consent) |
| `name` | Bank display name |
| `features.accounts` | Whether account data is supported |
| `features.balances` | Whether balance data is supported |
| `features.transactions` | Whether transaction data is supported |
| `features.statements` | Whether statement data is supported (`false` at Kiwibank and NZHL) |
| `features.party` | Whether party (account holder) data is supported |
## Caching
Metadata changes infrequently. Cache the response for up to **24 hours** to reduce API calls.
## Use Cases
| Use case | Product | How |
| -------- | ------- | --- |
| Bank selection UI | Both | Blink Payments: show only banks whose `redirect_flow` or `features.decoupled_flow` is enabled for the flow you use. Blink Data: show the banks in the response, which are those enabled for your organisation |
| Payment limits | Blink Payments only | Check the payment amount against `payment_limit.total` before submitting |
| Card payment availability | Blink Payments only | Check for the `Card` entry and its allowed card types and networks |
| Timeout configuration | Blink Payments only | Size your polling window from `request_timeout`. The Blink Data response has no timeouts |
| Permission availability | Blink Data only | Request only the permissions the bank supports, for example leave out `ReadStatements` where `features.statements` is `false` (otherwise 400 `BDT241`) |
# Glossary (https://merchants.blinkpay.co.nz/docs/shared/reference/glossary.md)
---
title: Glossary
description: Definitions of key BlinkPay and open banking terms
---
# Glossary
Key terms used in BlinkPay documentation and open banking, grouped by the product they apply to: terms used by both Blink Payments and Blink Data first, then terms specific to each product.
## Terms Used by Both Products
### API (Application Programming Interface)
A technical integration bridge between the services provided by Banks, BlinkPay and your application. An API defines the kinds of calls or requests that can be made, how to make them, the data formats that should be used, the conventions to follow, etc.
### Authentication
The verification that a user is valid.
### Authorisation
The rights and permissions that an authenticated user is allowed to access, based on an access policy.
### Bank
A financial institution that is authorised to perform monetary transactions and provide financial and other services to its Customers.
### Callback URL
An optional `callback_url` on a decoupled flow consent, on both products. BlinkPay sends it an unsigned `GET` with the consent ID as `cid` (`GET ?cid=`) when the customer authorises, the bank rejects the consent or the authorisation window ends. It carries no status: fetch the consent to read the outcome. It can arrive more than once, or not at all. **Blink Data:** the URL must be publicly resolvable, and use HTTPS in production.
### Consumed
A consent status meaning the consented action is complete; what that is depends on the product. **Blink Payments:** a single consent or quick payment is `Consumed` once the bank has made its final decision on the payment, which includes a payment the bank rejected after accepting it, so `Consumed` does not mean paid: read the payment's status. An enduring consent becomes `Consumed` once its expiry has passed. **Blink Data:** the consent's period has ended, because its `expiration_date_time` has passed. Data can no longer be read with it; create a new consent.
### Customer
A person, organisation or entity that holds an account with a Bank and authorises a consent for your application: to make a payment (Blink Payments) or to share their account data (Blink Data).
### Decoupled Flow (Pay by Mobile)
A workflow to gather consent from an Online Banking Customer typically using a mobile device. The customer can authorise on a different device or at a different time from their interaction with your application.
### Gateway Flow
A consent flow that takes the Customer through a BlinkPay gateway to choose their bank and complete their consent (and, for Blink Payments, the payment). It simplifies integration by handling bank selection and flow type selection. **Blink Payments only:** the Gateway is the only flow that supports card payments.
### Go-Live Date
The date your integration goes into production and can be used by your customers.
### JSON (JavaScript Object Notation)
A lightweight data-interchange format commonly used for communication between web systems.
### Merchant Category Code (MCC)
A 4-digit code that classifies a business's type. **Blink Payments:** assigned by the acquirer bank, used by card networks for transaction routing and reporting, and required for card payment onboarding. **Blink Data:** a transaction's `merchant.category` carries the MCC of the business the customer paid, where the bank supplies it.
### OAuth 2
The industry-standard protocol for Authorisation. BlinkPay uses OAuth 2.0 with the client_credentials grant type.
### Online Banking
The desktop and mobile internet banking experiences provided by Banks to Customers.
### Open Banking
The framework of APIs administered by Payments NZ and implemented by New Zealand Banks which enable access to underlying Bank functionality, e.g., data and payments.
### Personally Identifiable Information (PII)
Data that could be used to identify an individual person (e.g., name, address) and as otherwise defined by personal information in the Privacy Act 2020.
### Polling
Repeatedly reading a consent or payment until it reaches the status you are waiting for. Poll only once the customer has returned or the callback has fired, every second; in the direct decoupled flow with no callback, every 5 seconds. **Blink Payments:** poll a payment every second until it is `AcceptedSettlementCompleted` or `Rejected`, for a minute or two at most, then leave it to a periodic wash-up. Stop at a terminal state.
### Redirect Flow (Pay by Bank)
A workflow to gather consent from an Online Banking Customer typically using a web browser. The customer is redirected from your application to the Bank for authentication, then redirected back upon completion. It works on mobile too, where the bank's website or app opens.
### REST (Representational State Transfer)
An architectural style of HTTP web services, exposing uniform operations.
### UTF-8
A common standard for encoding of characters, supporting many character types.
## Blink Payments Terms
### Acquirer Bank
The bank that processes and settles card transactions on your behalf (e.g. BNZ, ASB). Required for card payment onboarding.
### Acquirer Merchant ID
15-character alphanumeric identifier assigned by your acquirer bank to uniquely identify your business for card payment settlement. Required for card payment onboarding.
### Card Not Present (CNP)
A card transaction type where the physical card is not presented at point of sale β applicable to all online/e-commerce card payments.
### Direct Credit
An electronic transfer of funds through the "Bulk Electronic Clearing System" (BECS) administered by Payments NZ. The payment is initiated by or on behalf of the payer, which sends funds directly into the bank account of the payee.
### Enduring Payment Consent
A consent type that allows a Merchant to debit funds from an Online Banking Customer on an ongoing basis, subject to agreed parameters such as maximum amounts and periods, which the Merchant sets and the Customer approves at their Bank.
### Fixed Payment Execution (FPE)
A single occurrence of a [Fixed Recurring Payment (FRP)](#fixed-recurring-payment-frp) schedule firing on a specific date β analogous to one individual debit on the 1st of the month under a direct debit mandate. Many FPEs exist per FRP. Each FPE is tracked independently with its own status, retries, and webhook events, and links to the payment it created.
### Fixed Recurring Payment (FRP)
The schedule that automatically initiates fixed-amount payments against an authorised enduring consent at a regular cadence β analogous to a direct debit mandate. The FRP holds the amount, start date, next payment date, retry strategy and status; its frequency comes from the consent's period, and it ends when it is cancelled or the consent is revoked or expires. Each scheduled execution against it is a [Fixed Payment Execution (FPE)](#fixed-payment-execution-fpe).
### Merchant
An organisation, person or entity that provides goods and/or services to Customers and receives payments in respect of those goods and/or services.
### One-off Payment
A payment made only once by a Customer to a Merchant. Uses a Single Consent or Quick Payment.
### PAN (Primary Account Number)
The 16-digit number on a credit or debit card. In card payments, PAN entry refers to the customer manually entering their card number, expiry, and CVV.
### Quick Payment
A single consent and its payment created in one call. In the redirect or gateway flow, retrieving it after the customer returns completes the payment; in the decoupled flow, BlinkPay submits the payment as soon as the customer authorises. A quick payment has at most one payment.
### Recurring Payment
A series of payments made by a Customer to a Merchant at a regular schedule. Uses an Enduring Payment Consent.
### Surcharging (Fee Forwarding)
Passing a payment fee on to the customer by adding a surcharge to the amount they pay. BlinkPay enables it per account on request.
### Variable Recurring Payment
A series of variable-amount Recurring Payments where the payment amount may differ each period within the limits of the Enduring Consent.
### Webhook
A signed event BlinkPay sends to an endpoint you subscribe to. Webhooks are sent only for Fixed Recurring Payment events (a scheduled payment completed or failed, or the schedule cancelled). There are no webhooks for quick, single or enduring payments, or for consent status changes: poll for those. Blink Data has no webhooks.
## Blink Data Terms
### BDT Error Codes
Blink Data error codes, of the form `BDTnnn` (for example `BDT410`), returned in the `code` field of the error body alongside `timestamp`, `path`, `status`, `error` and `message`. Branch on the HTTP status and `code`. Some 400 errors, such as a malformed body or a missing required field, have no `code`; read `message` for those. Blink Payments uses its own `BP` codes.
### Consent (Blink Data)
The customer's permission, given at their bank, for your application to read data from the accounts they choose to share. You create it with `POST /v1/consents`, naming the permissions you need and optionally an `expiration_date_time` and the `start_date_time`/`end_date_time` window of data it covers. Once `Authorised`, it can be used for repeated data calls until it is `Consumed` or `Revoked`.
### GatewayTimeout
A terminal consent status: a gateway consent was abandoned before the customer chose a bank, and the 15-minute bank-selection window ended. Create a new consent.
### Party
The account holder whose data you read. `GET /v1/party` returns the customer who authorised the consent, and `GET /v1/accounts/{account_id}/party` the holder of a specific account. Needs the `ReadParty` permission.
### Permission
A data type a Blink Data consent lets you read: `ReadAccounts`, `ReadBalances`, `ReadTransactions`, `ReadStatements` or `ReadParty`. The customer approves the permissions at their bank. A data call the consent has no permission for returns 403 `BDT411`, and a permission the bank doesn't offer (such as `ReadStatements` at Kiwibank) is rejected with 400 `BDT241` when you create the consent. Permissions belong to the consent; scopes belong to your API client.
### Revoked
A terminal consent status: the consent has been cancelled, either by you (`DELETE /v1/consents/{consent_id}`) or by the customer at their bank. Data calls with it return 403 `BDT410` (or `BDT415` when it was revoked at the bank).
### Statement
A statement the bank has issued for an account and period. You can list an account's statements, retrieve one, and download its file (PDF or plain text, depending on the bank). Available at ANZ, ASB, BNZ and Westpac (and PNZ in the sandbox), not at Kiwibank or NZHL. Needs the `ReadStatements` permission.
# Postman Collection (https://merchants.blinkpay.co.nz/docs/shared/reference/postman.md)
---
title: Postman Collection
description: Test BlinkPay APIs using our Postman workspaces
---
# Postman Collection
Test and explore BlinkPay APIs using our Postman collections.
## Workspaces
| API | Postman Workspace |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| **Payment API (Blink Payments)** | [blinkpay-merchant-workspace](https://www.postman.com/blinkpay/blinkpay-merchant-workspace/overview) |
| **Data API (Blink Data)** | [blinkpay-organisation-workspace](https://www.postman.com/blinkpay/blinkpay-organisation-workspace/overview) |
## Getting Started
### 1. Download Collection and Environment
Visit the appropriate workspace above and download both the collection and sample environment files to your local Postman.
### 2. Configure Environment Variables
Get your credentials from the [Client Portal](https://merchants.blinkpay.co.nz/) under **Settings > API Settings**: open the environment's row, copy the API Client ID and select **Rotate Secret** to obtain the client secret (it is shown only once). See [Getting API Credentials](/docs/shared/authentication#getting-api-credentials). Then update the environment variables:
| Variable | Value |
| --------------- | -------------------------- |
| `base_url` | See environment URLs below |
| `client_id` | Your OAuth client ID |
| `client_secret` | Your OAuth client secret |
**Environment URLs:**
| API | Sandbox | Production |
| ----------- | -------------------------------------- | ------------------------------ |
| Payment API | `https://sandbox.debit.blinkpay.co.nz` | `https://debit.blinkpay.co.nz` |
| Data API | `https://sandbox.data.blinkpay.co.nz` | `https://data.blinkpay.co.nz` |
### 3. Generate Access Token
1. Run the **Authentication > Generate access token** request
2. The `access_token` variable is automatically set
3. Token is valid for 1 hour
## Sandbox Testing
Use the mock bank "PNZ" for testing:
| Flow | Credentials |
| ------------- | -------------------------------------- |
| **Redirect** | Username: `user01`, Password: `password` |
| **Decoupled** | Phone: `+64-259531933` (auto-approves) |
Any other mobile number is accepted but never authorises: the consent stays `AwaitingAuthorisation` until the bank's window ends, then becomes `Rejected`. To produce a decline, choose **Cancel** on the PNZ login page before logging in. To test revocation, authorise with the auto-approve number, then call the DELETE endpoint for the consent.
# AI Coding Tools (MCP) (https://merchants.blinkpay.co.nz/docs/shared/reference/ai-tools.md)
---
title: AI Coding Tools (MCP)
description: Connect Claude Code, Cursor or any MCP client to the BlinkPay documentation and API specifications
---
# AI Coding Tools (MCP)
BlinkPay publishes its developer documentation and API specifications through a [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server, so the AI assistant in your IDE can look things up while you build your integration, instead of you copying between a browser and your editor. It reads the same documentation you see on this site, so its answers are always current.
No account, API key or Client Portal login is needed. The server is retrieval only: it returns documentation and API schemas, and has no access to any payment, consent or account data.
## Endpoint
| Setting | Value |
| -------------- | -------------------------------------- |
| Endpoint | `https://merchants.blinkpay.co.nz/mcp` |
| Transport | Streamable HTTP |
| Authentication | None |
## Setup
### Claude Code
Add the server from your project directory:
```bash
claude mcp add --transport http blinkpay-docs https://merchants.blinkpay.co.nz/mcp
```
To share it with everyone working on the project, commit a `.mcp.json` file at the root of your repository instead:
```json
{
"mcpServers": {
"blinkpay-docs": {
"type": "http",
"url": "https://merchants.blinkpay.co.nz/mcp"
}
}
}
```
### Cursor
Add the server to `.cursor/mcp.json` in your project, or to `~/.cursor/mcp.json` to use it in every project:
```json
{
"mcpServers": {
"blinkpay-docs": {
"url": "https://merchants.blinkpay.co.nz/mcp"
}
}
}
```
### Other clients
Any client that supports the Streamable HTTP transport can connect with the endpoint above. Once connected, your client should list five tools from `blinkpay-docs`.
## Getting the best results
The server tells your assistant most of this when it connects. It is here so you can check it is being followed.
- **Load the full documentation first.** For a new integration, have your assistant load everything for your product with `get_docs` (`api` `debit` for Blink Payments, `data` for Blink Data), then use `search_docs` for follow-up questions. The documentation is small enough to read whole, and reading it whole surfaces the failure paths nobody thinks to search for. Without MCP, the same content is at [`/docs/debit/llms-full.txt`](https://merchants.blinkpay.co.nz/docs/debit/llms-full.txt) and [`/docs/data/llms-full.txt`](https://merchants.blinkpay.co.nz/docs/data/llms-full.txt).
- **Ask for part of a large schema.** Some API operations are large. `get_openapi_operation` with `sections` set to `request` or `responses` returns only the part you need.
- **Use an SDK on your server.** Where an official BlinkPay SDK exists for your backend language (listed on the SDKs page of the Blink Payments documentation, and on [GitHub](https://github.com/orgs/BlinkPay/repositories)), use it. Blink Data has no SDK, so call it over HTTPS from your server. Never put your client credentials in a mobile app or a browser: token requests and API calls belong on your server.
- **Mobile apps: start from the Flutter demo.** Whatever you build in, including React Native, Swift or Kotlin, read the [BlinkPay Flutter Demo](https://github.com/BlinkPay/BlinkPay-Flutter-Demo) as the reference for the gateway flow, a backend that holds your credentials, opening the bank in an in-app browser (SFSafariViewController or Chrome Custom Tabs) and handling the return to your app. [Mobile Integration](/docs/shared/flows/mobile-integration#reference-implementation) lists the demo's shortcuts that a real app must not copy.
- **Try requests in Postman.** The [Postman page](/docs/shared/reference/postman) links the public workspaces for both APIs.
## Integration Standards
[Credential and Integration Security](/docs/shared/standards/credential-security) and [Consent Experience for Direct Flows](/docs/shared/standards/consent-experience) set out what BlinkPay expects of every integration: how to keep credentials out of apps and repositories, and what to show customers when you build your own consent screens. Asking it to check your integration against both pages before going live is a good use of the server.
## Tools
You rarely need to call these yourself. Ask your assistant a question such as _"Using the BlinkPay docs, how do I create a quick payment with the gateway flow, and what does the request body look like?"_ for Blink Payments, or _"Using the BlinkPay docs, how do I create a Blink Data consent with the gateway flow and then list the customer's accounts?"_ for Blink Data, and it will choose the tools it needs.
| Tool | Returns |
| ----------------------- | -------------------------------------------------------------------------------- |
| `get_docs` | A product's full documentation as markdown, in parts |
| `list_docs` | Every documentation page with its path, title, description and section |
| `search_docs` | The best-matching documentation pages, ranked, each with its best section linked |
| `get_doc` | One documentation page as markdown, exactly as published |
| `get_openapi_operation` | The fully resolved schema of one API operation, or the list of operations |
### get_docs
Returns every page for `api` `debit` (Blink Payments and the shared pages), `data` (Blink Data and the shared pages) or `all`, in the order of the sidebar, as the same markdown the `.md` URLs serve. The result comes in parts of at most about 45 KB (roughly 17,000 tokens), so each fits within MCP clients' limits on a single tool result; each part says which part it is and how many there are, and the optional `part` argument (from 1) asks for the next one. A page is never split across parts unless it is larger than a part on its own.
```json
{ "api": "debit" }
```
```json
{ "api": "debit", "part": 2 }
```
### list_docs
Lists every page in sidebar order with its path, title, description and section (`debit`, `data` or `shared`). The optional `api` argument limits the list in the same way as `search_docs`.
```json
{ "api": "data" }
```
### search_docs
Searches the documentation and returns the best-matching pages, one result per page, each with the path of its best-matching section (with the heading anchor), the headings above it, and a short snippet. It tolerates typos and partial words, so `idempot` finds idempotency. The optional `api` argument limits results to `debit` (Blink Payments) or `data` (Blink Data), each including the pages shared by both, or to `shared` alone, which helps when both products have a page of the same name.
```json
{ "query": "idempotency key", "limit": 3 }
```
```json
{ "query": "consent", "api": "debit" }
```
The first result for this query is `/docs/shared/help/troubleshooting#idempotency-keys`, which can be passed straight to `get_doc`.
### get_doc
Returns one page as markdown. It accepts a page path, the same path with `.md` appended, or a `search_docs` result path with its `#anchor`. The anchor is accepted but the whole page is always returned, so nothing around the matched section is lost; it is the same content as the page's `.md` URL. `/docs/llms.txt` returns the index of every page.
```json
{ "path": "/docs/shared/flows/gateway-flow" }
```
### get_openapi_operation
Returns one operation from the OpenAPI specifications, with every `$ref` expanded so the complete request and response schemas are in one place. `api` is `debit` for Blink Payments or `data` for Blink Data; operation IDs repeat across the two, so it is required. Leave out `operationId` to list every operation of that API with its method, path and summary. The optional `sections` argument returns only the `request` (parameters and body) or only the `responses`, when the whole operation is more than you need.
```json
{ "api": "debit", "operationId": "create-quick-payment" }
```
```json
{ "api": "debit", "operationId": "create-quick-payment", "sections": "request" }
```
```json
{ "api": "data" }
```
## Markdown without MCP
If your tools don't support MCP, append `.md` to any documentation URL to get the page as markdown, ready to paste into a prompt or fetch from an agent:
```text
https://merchants.blinkpay.co.nz/docs/shared/flows/gateway-flow -> rendered page
https://merchants.blinkpay.co.nz/docs/shared/flows/gateway-flow.md -> markdown
```
[`/docs/llms.txt`](https://merchants.blinkpay.co.nz/docs/llms.txt) lists every page with its markdown URL, so an agent can find the right page without crawling the site. The whole documentation is also available as one file per product, at [`/docs/debit/llms-full.txt`](https://merchants.blinkpay.co.nz/docs/debit/llms-full.txt) (Blink Payments and the shared pages), [`/docs/data/llms-full.txt`](https://merchants.blinkpay.co.nz/docs/data/llms-full.txt) (Blink Data and the shared pages) and [`/docs/llms-full.txt`](https://merchants.blinkpay.co.nz/docs/llms-full.txt) (everything).
## API Specifications and SDKs
The full OpenAPI specifications are published as YAML at [`/docs/debit/openapi.yaml`](https://merchants.blinkpay.co.nz/docs/debit/openapi.yaml) (Blink Payments) and [`/docs/data/openapi.yaml`](https://merchants.blinkpay.co.nz/docs/data/openapi.yaml) (Blink Data), with no sign-in needed. The Resources page of the [Client Portal](https://merchants.blinkpay.co.nz/resources) renders the same specifications once you are signed in. Client libraries are on [GitHub](https://github.com/orgs/BlinkPay/repositories), and the [Postman Collection](/docs/shared/reference/postman) covers trying requests by hand.
## Limits on this documentation server
These limits apply only to this documentation server, not to the Blink Payments or Blink Data APIs; for how to handle the APIs' `429` responses, see [Troubleshooting](/docs/shared/help/troubleshooting#retry-logic). The APIs' own rate limits are described under [API Rate Limits](/docs/shared/reference/api-policies#api-rate-limits).
Requests to the documentation server are limited per IP address, and the server may answer HTTP `429` with a `Retry-After` header; retry once that time has passed. Everyone behind the same office network address shares one allowance.
# Credential and Integration Security (https://merchants.blinkpay.co.nz/docs/shared/standards/credential-security.md)
---
title: Credential and Integration Security
description: How to hold BlinkPay API credentials, tokens and redirect URLs safely, and how to confirm outcomes you can trust
---
# Credential and Integration Security
These are the standards BlinkPay expects every Blink Payments and Blink Data integration to meet. Most of them cost nothing to adopt at the start and are expensive to retrofit, so build them in from your first sandbox call.
## Keep Credentials on Your Server
Your client ID and client secret belong on a server you control, never in a mobile app, a browser, a single-page application or any other front end. Anything shipped to a customer's device can be extracted from it. Your backend holds the credentials, requests access tokens and makes every BlinkPay API call. It is also the only place that decides what the customer is asked to approve: for Blink Payments, the amount and the payment reference (particulars, code and reference); for Blink Data, the permissions and the access period. Never accept those values from the app or the browser as they arrive.
Your app or web front end talks to your own backend, authenticated with your own user session or login tokens, not with BlinkPay credentials. [Mobile Integration](/docs/shared/flows/mobile-integration) shows this architecture for native apps.
## Store Secrets in a Secret Manager
Never commit credentials to a repository, including private ones, and never paste them into tickets, chat or email. Keep them in a secret manager or secure parameter store, such as AWS Secrets Manager or SSM Parameter Store, Azure Key Vault or GCP Secret Manager, and inject them into your application at runtime. Limit who and what can read them, and keep sandbox and production credentials in separate entries so a test system can never pick up production keys.
The portal never displays an existing secret. Selecting **Rotate Secret** under **Settings > API Settings** generates a new one and shows it once, so copy it straight into your secret store (see [Getting API Credentials](/docs/shared/authentication#getting-api-credentials)).
## Rotate Deliberately
Rotate the client secret when someone with access to it leaves, whenever you suspect it has been exposed, and periodically as part of your own key management. Only users with the Administrator or Developer role can rotate a secret, so review who holds those roles when staff change. When a production secret is rotated, BlinkPay emails your organisation's Administrators and Developers, which also tells you if someone rotates it unexpectedly.
Rotation takes effect immediately and there is no grace period: the old secret stops working for new token requests at once, while access tokens already issued remain valid until they expire. Plan the switch before you select **Rotate Secret**, so the new value goes into your secret store and your services pick it up straight away. See [Credential Rotation](/docs/shared/authentication#credential-rotation).
## Handle Tokens Carefully
Request an access token, cache it on your server and reuse it until shortly before it expires, rather than requesting one per API call (see [Token Caching](/docs/shared/authentication#token-caching)). Keep tokens in server memory or your secret store, never in a browser, a mobile app, a URL or a log.
## Request Only the Scopes You Need
Scopes are assigned to your API client by BlinkPay, based on the services you select when you apply, and the access token response lists the ones you hold. Select only the services you will use, and ask [support@blinkpay.co.nz](mailto:support@blinkpay.co.nz) to remove access you no longer need. A leaked credential can only do what its scopes allow.
## Use HTTPS Everywhere
Every BlinkPay API call uses HTTPS, and requests over plain HTTP are refused. Use HTTPS for your own pages and endpoints that take part in the flow too: your return pages and, for the decoupled flow, your `callback_url`. Production rejects `http://` redirect URLs.
## Register Redirect URLs Narrowly
Register the specific return path you use, not a bare domain. Path matching is by prefix on the raw string, so registering `https://shop.example.com/` accepts every page on that host and `https://shop.example.com/pay` also accepts `/payment-admin`. Register a path that ends in a distinct segment, such as `https://shop.example.com/blinkpay/return`, use a wildcard subdomain only when you need one, and remove entries you no longer use. Sandbox and production keep separate lists, so a test URL registered in sandbox never applies to production. The matching rules are in [Redirect URI Whitelisting](/docs/shared/authentication#redirect-uri-whitelisting).
## Confirm Every Outcome Server-Side
The return redirect is a prompt to check, not proof of anything. Anyone can type your return URL with a `cid` and no `status`, and a genuine return may never arrive if the customer closes the browser. When the customer comes back, check that the `cid` matches the consent ID (or quick payment ID) you stored for that order, then call the API from your backend to read the actual status before you act on it. Branch on the `status` parameter, never on the `error` text, and never treat a value you don't recognise as success. The parameters are described in [Return Parameters](/docs/shared/flows/redirect-flow#return-parameters).
The decoupled flow's `callback_url` request is unsigned and carries only the consent ID, so treat it the same way: use it as a signal to read the consent from the API, never as the outcome itself.
## Don't Log Secrets or Tokens
Never log the client secret, access tokens or `Authorization` headers, and make sure your HTTP client, proxy and error-reporting tools don't capture them either. If you must record that a credential was used, mask it (for example, the last four characters). Treat customer details such as mobile numbers, account numbers and, for Blink Data, the account information you retrieve as personal information: log only what you need, masked where you can. To trace a Blink Payments request, log the `request-id` response header instead; support can find the request from it.
## Use Idempotency Keys
**Blink Payments only:** send an `idempotency-key` header on every request that creates a consent, payment or refund. The value must be a UUID; anything else is rejected with 400. Use a fresh key for each distinct payment and the same key when you retry the same request after a timeout or a 5xx, so a retry can never take the money twice. Gateway flow consents are de-duplicated on the `request-id` header instead, so send a UUID `request-id` with those and reuse it on a retry.
# Consent Experience for Direct Flows (https://merchants.blinkpay.co.nz/docs/shared/standards/consent-experience.md)
---
title: Consent Experience for Direct Flows
description: What your application must show customers when it uses the redirect or decoupled flow directly instead of the BlinkPay gateway
---
# Consent Experience for Direct Flows
When you use the [Gateway Flow](/docs/shared/flows/gateway-flow), BlinkPay provides the screens a customer sees between your checkout and their bank. When you use the [Redirect Flow](/docs/shared/flows/redirect-flow) or [Decoupled Flow](/docs/shared/flows/decoupled-flow) directly, you build those screens yourself, and getting them right becomes your responsibility. This page sets out what that involves, for Blink Payments and Blink Data alike.
## What the Gateway Provides
| Gateway feature | What it does |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Bank selection | Lists the banks enabled for the consent, by name and logo |
| Flow selection | Offers the ways the customer's bank supports on their device: the bank's internet banking website, opening the bank's app, or sending the request to the bank's app (the redirect and decoupled flows), including the waiting screen and countdown while a decoupled request is approved in the app |
| Confirmation screen | **Blink Payments:** shows who is being paid, the amount and the payment reference (particulars, code and reference) before the customer goes to their bank, with any surcharge and the total where surcharging is enabled for your account |
| Bank limit warnings | **Blink Payments:** warns the customer when the amount is over their bank's payment limit and asks them to choose another bank, and shows ANZ's notice that Voice ID is needed for payments over $10,000 |
| Opening the banking app | On a mobile device, opens the customer's banking app directly for the banks that support it; other banks continue in the browser |
| Timeouts and pending states | Counts down the bank's authorisation window and tells the customer when an outcome is still being confirmed |
When the customer has finished, the gateway returns them to your `redirect_uri` in the browser. If your front end is a mobile app, make that a link that opens your app (see [Register Your Return Link](/docs/shared/flows/mobile-integration#register-your-return-link)).
## Your Responsibilities with Direct Flows
With a direct flow, you are responsible for following the Payments NZ API Centre [Customer Experience Guidelines v3.0](https://paymentsnz.atlassian.net/wiki/spaces/PaymentsNZAPIStandards/pages/2173141006/Customer+Experience+Guidelines+v3.0), the industry standard for how payment and account access consents are presented to New Zealand customers. Read them before you design your screens. The sections below are BlinkPay's own guidance, and they do not replace the guidelines. Much of it is what the gateway does for you (see [What the Gateway Provides](#what-the-gateway-provides)); with the gateway, only [Showing the Outcome](#showing-the-outcome) and [Letting Customers Revoke](#letting-customers-revoke) remain yours.
## Before Sending the Customer to the Bank
The gateway's confirmation screen covers this. With a direct flow, show the customer exactly what they are about to approve, on a screen they confirm before you create the consent or send them to their bank. Their bank shows the request again, and the two should match.
### What to Show the Payer
**Blink Payments:** for a PayNow payment, show the payee as your customers know you, the amount, and the reference that will appear on their bank statement. If you add a surcharge, show it and the total separately before the customer proceeds. For AutoPay, show who will take the payments, the maximum per period and the period itself (for example weekly or monthly), any maximum for a single payment, when the agreement starts and when it ends, and how the customer can cancel it.
### What to Show for Data Access
**Blink Data:** show which information you will access (for example account details, balances, transactions or statements), why you need it, how long your access lasts, and for transactions the date range you will read. Ask only for the permissions you use.
### Explaining the Next Step
Tell the customer what happens next. With the redirect flow they go to their bank's website or app and come back to you afterwards. With the decoupled flow the request goes to their banking app: some banks send a push notification, and with others the customer opens the app and checks its notifications or messages. In every flow the customer chooses the account at their bank, so never ask for an account number on your own screens.
## Presenting Bank Choice
Offer every supported bank your account has enabled, using the bank names and logos customers recognise, and don't steer customers towards one bank. [Bank Coverage](/docs/shared/bank-coverage) lists the banks, the flows each supports and, for the decoupled flow, the identifier each accepts (see [Decoupled Flow Identifiers](/docs/shared/bank-coverage#decoupled-flow-identifiers)).
**Blink Payments:** check the amount against the bank's limit before the customer chooses, so they can pick another bank rather than fail at theirs. The limits are in [Payment Limits](/docs/shared/bank-coverage#payment-limits), and `GET /payments/v1/meta` returns each bank's current `payment_limit` at runtime. Tell ANZ customers that ANZ needs Voice ID for payments over $10,000.
## Showing the Outcome
Only show success once your backend has confirmed it with the API. The return redirect and the decoupled callback tell you when to look, not what happened (see [Credential and Integration Security](/docs/shared/standards/credential-security#confirm-every-outcome-server-side)). While you check, show a "checking your payment" or "waiting for your bank" state, not a failure.
**Blink Payments:** keep three states apart. A payment is **pending** while it is `Pending` or `AcceptedSettlementInProcess`, **paid** once it is `AcceptedSettlementCompleted`, and **failed** if it is `Rejected`. An `Authorised` consent means the customer approved it, not that money has moved. Most payments move within seconds; check every second until the payment reaches `AcceptedSettlementCompleted` or `Rejected`, pick up any still in flight with a periodic wash-up, and contact support with the consent ID and payment ID if one is still `Pending` after 6 hours. BlinkPay does not send receipts to your customers, so confirm the payment to them yourself.
**Blink Data:** the consent is `Authorised` when the customer has granted access. If it is `Rejected`, or the bank's authorisation window passed, tell the customer nothing was shared and offer to try again.
Don't poll while the customer is at their bank. Once they have returned, or the decoupled callback has fired, check the consent every second until it leaves `AwaitingAuthorisation`; in the direct decoupled flow with no callback, check every 5 seconds. If the customer returned with a non-success `status`, you can show the accompanying `error` text, which is written for customers, but base your own logic on `status` and the API.
## Letting Customers Revoke
Customers can always revoke a consent at their bank, through internet banking or the bank's app. Give them a way to do it with you as well, through the same channel they used to set it up, such as the account or settings area of your website or app, and show them which consents are active. Revoke the consent through the API when they ask (**Blink Payments:** `DELETE` on the single or enduring consent; **Blink Data:** `DELETE /v1/consents/{consent_id}`).
A revocation made at the bank is not pushed to you: read the consent before relying on it, and it shows as `Revoked`. Check before relying on a consent, and tell the customer if an AutoPay agreement or data access has ended.
Before you go live, check your screens against the [Going Live](/docs/shared/help/going-live) checklist.
# Troubleshooting (https://merchants.blinkpay.co.nz/docs/shared/help/troubleshooting.md)
---
title: Troubleshooting
description: Common issues, solutions, and debugging techniques
---
# Troubleshooting
This guide covers common issues you may encounter when integrating BlinkPay, along with solutions and debugging strategies.
## Common Errors
### HTTP Error Codes
| 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](#_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](#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](/docs/shared/reference/api-policies#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 |
## Authentication Issues
### 401 Unauthorised
**Symptom:** All API requests return 401
**Causes:**
- Access token expired (lifetime: 1 hour)
- Invalid client credentials
- Environment mismatch (sandbox credentials in production)
**Solutions:**
1. Implement automatic token refresh - cache token with expiry, refresh 5 minutes before expiration
2. On 401 response: request new token and retry the original request
3. Verify credentials match your environment (sandbox vs production)
### 403 Forbidden
**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.
## Consent Issues
### Consent Stuck in AwaitingAuthorisation
**Symptom:** Consent doesn't progress after redirect
**Causes:**
1. Customer didn't complete authorisation
2. Customer closed the bank app or browser without finishing
3. Bank system issue
4. Network timeout
**Solutions:**
1. Once the customer has returned (or the decoupled callback has fired), poll the consent every second until it leaves `AwaitingAuthorisation`; in the direct decoupled flow with no callback, every 5 seconds
2. Check for terminal states: a consent waiting at the bank ends as either `Authorised` or `Rejected`
3. Display appropriate UI to customer while waiting
What 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:**
- Clearly explain the process to customers
- Minimise steps before redirect
- Provide clear instructions
- Consider decoupled flow for known customers
### Consent Times Out Before Authorisation
**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](/docs/shared/reference/metadata#payment-metadata-fields) 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:**
- Display user-friendly message: "Your session expired. Please try again."
- Offer retry option that creates a new consent
- Log the timeout for monitoring
**Prevention:**
- Streamline checkout flow
- Explain process upfront
- Set customer expectations
- Consider decoupled flow for complex cases
### Consent Rejected
**Symptom:** Consent status becomes 'Rejected'
**Common Causes:**
- Customer clicked "Cancel" or "Decline"
- Customer failed authentication multiple times
- Account doesn't support requested operation
- Insufficient funds (for payment consents)
- Bank fraud detection triggered
**Handling:**
- Display a clear error message. The API has no rejection-reason field; on a redirect or gateway return, the `status` parameter gives the kind of outcome and `error` carries text you can show (see [Return Parameters](/docs/shared/flows/redirect-flow#return-parameters))
- Suggest: check account balance, use different account, contact bank
- Offer retry option or alternative payment method
**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.
## Payment Issues
**Blink Payments only.**
### Payment Stuck in Pending
**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:**
1. Log the issue with payment ID and age
2. Contact BlinkPay support with the consent ID, payment ID and request ID
3. Monitor for status change
### Payment Rejected
**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:**
- Log the rejection for analysis
- Display a user-friendly message. The payment carries no rejection reason, so suggest the customer checks with their bank
- Offer retry or alternative payment method. A new attempt needs a new `idempotency-key`
## Integration Issues
### Redirect URI Not Working
**Symptom:** 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:**
- Protocol mismatch: `http://` vs `https://`
- Registering `/callback/` and sending `/callback`
- Subdomain differences: `yourapp.com` vs `www.yourapp.com` β consider using a wildcard entry like `https://*.yourapp.com/callback`
- Port number mismatch
- Registering in sandbox but calling production (each environment has its own list)
**Debugging:**
- Log callback requests to see the full URL received
- Check for the `cid` query parameter, which carries the consent ID (see [Return Parameters](/docs/shared/flows/redirect-flow#return-parameters))
- Verify the callback URL in browser network tools
### CORS Errors
**Symptom:** Browser console shows CORS errors
**Root Cause:** BlinkPay API calls must be made from your **backend server**, not browser JavaScript
**Solution:**
1. Frontend calls your backend API
2. Backend calls BlinkPay API
3. Backend returns result to frontend
Never expose your client credentials or make BlinkPay API calls directly from browser code.
### Mobile Deep Linking Issues
**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:**
- Add logging to your deep link handler
- Verify URL scheme, host, and query parameters
- Test on actual devices (not simulators)
See [Mobile Integration Guide](/docs/shared/flows/mobile-integration) for complete details.
## Validation Errors (400)
### Common Validation Issues
**Invalid date format:**
- **Blink Payments:** wrong `"from_timestamp": "2025-01-01"`; correct `"from_timestamp": "2025-01-01T00:00:00+13:00"` (ISO 8601 with timezone)
- **Blink Data:** the consent dates (`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):**
- A string with a decimal point and one or two decimal places, greater than zero, in NZD: `"100.00"` or `"100.5"`, not `"100"`. Send two decimal places
**PCR fields (Blink Payments):**
- `particulars` is required (1-12 characters); `code` and `reference` are optional (up to 12 characters each)
- Allowed characters: letters, digits, space and `- & # ? : _ / , . '`. Anything else is rejected, not stripped
- Example: `"particulars": "INV12345"`, `"code": "SALE"`, `"reference": "CUST001"`
**Invalid bank:**
- Both products take the same case-sensitive `bank` values: `ANZ`, `ASB`, `BNZ`, `Kiwibank`, `NZHL`, `Westpac`, and `PNZ` in the sandbox
- **Blink Data:** a value the API doesn't recognise returns 400 `BDT243`. 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:**
- Error: "Redirect URI not whitelisted"
- Solution: Register the URI in the client portal under Settings > API Settings, for the environment you're calling (sandbox, production). You can add an exact URI or a wildcard subdomain entry (e.g., `https://*.example.com/callback`) to match multiple subdomains. This requires the Developer or Administrator role β otherwise email [support@blinkpay.co.nz](mailto:support@blinkpay.co.nz) with the URI(s) you need registered.
## Retry Logic
### When to Retry
| 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](/docs/shared/reference/api-policies#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 |
### Retry Strategy
For 5xx errors, use exponential backoff:
- **Max retries:** 2
- **Initial delay:** 0.5 seconds
- **Multiplier:** 2x (delays: 0.5s, 1s)
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
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):**
- Use a unique UUID per distinct request (Blink Payments rejects a key that is not a UUID with `400`)
- Keys are cached for 24 hours, but the binding to the payment they created is permanent
- Always include when retrying payment creation requests
- Gateway-flow consents are de-duplicated on the `request-id` header rather than `idempotency-key`, so resend the same `request-id` when retrying one
**Blink 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 |
## Bank Maintenance
Banks may have scheduled maintenance windows, typically overnight (NZ time).
### How Maintenance Affects Payments
**Blink Payments:**
| Flow Type | Behaviour During Maintenance |
|--------------------------|------------------------------------------------------------------------|
| **Gateway** | Affected bank is hidden from bank selection |
| **In-progress payments** | `AcceptedSettlementInProcess` payments complete after maintenance ends |
### Handling Maintenance
- **Don't** automatically retry payments that are `AcceptedSettlementInProcess`
- **Don't** treat a payment as successful until it reaches `AcceptedSettlementCompleted`
- **Do** allow additional time for overnight payments to settle before treating as failed
**Blink 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.
## Debugging Techniques
### Request Headers for Debugging
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.
### Logging Best Practices
For debugging and audit purposes:
- Log all API request/response pairs (sanitise sensitive data)
- Record the `request-id` returned in response headers
- Track request duration for performance monitoring
- Never log full access tokens or client secrets
## Getting Support
If issues persist after troubleshooting, contact [support@blinkpay.co.nz](mailto:support@blinkpay.co.nz) with your environment, consent/payment IDs, and steps to reproduce.
[View full support resources β](/docs/shared/help/support)
## Service Status
Check system status: [www.blinkpay.co.nz/service-status](https://www.blinkpay.co.nz/service-status)
Sign up for updates on that page to be notified of any incidents.
# Going Live (https://merchants.blinkpay.co.nz/docs/shared/help/going-live.md)
---
title: Going Live
description: Production readiness checklist for BlinkPay integration
---
# Going Live
This guide covers the process of taking your BlinkPay integration from sandbox to production.
## Production Readiness Checklist
### Integration
- [ ] Consent creation and authorisation working
- [ ] **Blink Payments:** payment initiation and status polling working
- [ ] **Blink Data:** consents created and their status checked with `GET /v1/consents/{consent_id}`, account data retrieved with the `consent_id`, and revocation with `DELETE /v1/consents/{consent_id}` working
- [ ] Each flow you use (Gateway/Redirect/Decoupled) tested for every outcome the sandbox can produce: approval, decline, timeout and revocation
- [ ] Where required, handling in place for outcomes the sandbox cannot produce (bank errors, revocation at the bank, and for Blink Payments a payment rejected by the bank after authorisation), built from the documented statuses and error codes
- [ ] Return URL handling correct, with every outcome confirmed by a server-side GET
- [ ] Error handling implemented (401 token refresh, 5xx retry with backoff, timeouts)
- [ ] Tested with mock bank (PNZ) in sandbox
- [ ] **Blink Payments:** `AcceptedSettlementCompleted` is the only status treated as success (it means the payer's bank has confirmed sending the funds), never the return redirect or the consent status alone
- [ ] **Blink Payments:** your own payment confirmation or receipt sent to the customer. BlinkPay does not send receipts to your customers.
### Card Payments (Blink Payments, if applicable)
- [ ] Card payment onboarding completed (acquirer merchant ID, MCC, acquirer bank provided to BlinkPay)
- [ ] Card payment feature confirmed enabled via metadata endpoint
- [ ] Card payment flow tested in sandbox with test card numbers
### Security
- [ ] API credentials stored securely (environment variables or secret manager)
- [ ] No credentials in source control
- [ ] All API calls use HTTPS
- [ ] Bearer tokens implemented correctly
- [ ] No tokens exposed in client-side code
- [ ] [Credential and Integration Security](/docs/shared/standards/credential-security) guidance followed
### User Experience
- [ ] Clear instructions displayed: what the customer is paying, or what account data they are sharing and for how long
- [ ] Loading states implemented
- [ ] User-friendly error messages
- [ ] Success confirmation displayed
- [ ] Mobile responsive design
### Consent Management (Blink Data)
- [ ] Customers can view active consents
- [ ] Customers can revoke consents through same channel used for creation
- [ ] Consent expiry handling implemented
- [ ] Expired/revoked consent detection in place
### Monitoring
- [ ] Signed up for service status updates: [blinkpay.co.nz/service-status](https://www.blinkpay.co.nz/service-status)
- [ ] Consent and payment IDs logged for troubleshooting, and for Blink Payments the `request-id` of each call
- [ ] Error rates monitored
- [ ] **Blink Payments:** payments still `Pending` are followed up. Most payments move within seconds; if one is still `Pending` after 6 hours, contact support with the consent ID and payment ID
### Compliance (Direct Flows Only)
If using Redirect/Decoupled flows directly (not Gateway):
- [ ] Payments NZ Customer Experience Guidelines compliance verified (see [Consent Experience for Direct Flows](/docs/shared/standards/consent-experience))
- [ ] `x-customer-ip` and `x-customer-user-agent` headers implemented
- [ ] **Blink Data:** privacy policy updated to cover account data access
## Requesting Production Access
Production access is requested from the same portal you use for sandbox, at [merchants.blinkpay.co.nz](https://merchants.blinkpay.co.nz/).
### Step 1: Select "Upgrade to Production"
Log in and select **Upgrade to Production**, in the top navigation bar or on **Settings > API Settings**. Anyone in your organisation can see the button, but only an Administrator can submit the application.
### Step 2: Submit the Application
The application asks for your business details, the services you need, banking and billing details and a security questionnaire, then for you to review it and accept the Client Service Agreement.
### Step 3: Production Upgrade Approval
BlinkPay reviews your business and integration needs. Production upgrade approval usually takes 1-3 business days.
### Step 4: Get Your Production Credentials
Once approved, open **Settings > API Settings** and open the **Production** row. Copy the API Client ID, then select **Rotate Secret** to obtain the client secret. The portal shows the secret only once, so store it in your secret manager straight away.
Base URLs:
- Blink Payments: `https://debit.blinkpay.co.nz`
- Blink Data: `https://data.blinkpay.co.nz`
### Step 5: Configure and Check Production
Production is configured separately from sandbox. Before you launch:
1. **Register your production redirect URLs.** The production whitelist is separate from the sandbox one: add them under **Settings > API Settings**, Production row, **Whitelisted Redirect URLs**. Production rejects `http://` and `localhost` URLs. **Blink Payments:** if you use Fixed Recurring Payment webhooks, create the subscription again in production; its callback URL must also be on the production whitelist.
2. **Confirm which banks are enabled for your account** by calling the metadata endpoint with your production credentials (**Blink Payments:** `GET /payments/v1/meta`; **Blink Data:** `GET /v1/meta`). See [Metadata](/docs/shared/reference/metadata).
3. **Make a low-value live payment** (or, for Blink Data, a live consent) as a final smoke test, and check it reaches its expected final status.
## Production Configuration
Configure your application to use production endpoints and credentials:
- Base URL for Blink Payments: `https://debit.blinkpay.co.nz`
- Base URL for Blink Data: `https://data.blinkpay.co.nz`
- Store Client ID and Client Secret in environment variables
**Use Environment Variables**
Never hard-code production credentials. Always use environment variables or a secure secret management system.
## Key Metrics to Monitor
Track these metrics to understand your integration performance:
- Payment success rate (Blink Payments)
- API error rate
- Consent authorisation rate
- Average time to completion
# Demo Applications (https://merchants.blinkpay.co.nz/docs/shared/help/demos.md)
---
title: Demo Applications
description: Live examples and reference implementations
---
# Demo Applications
Explore BlinkPay's capabilities through our live demo applications and reference implementations.
## Live Payment Demos
### ACME Payments Demo (Sandbox)
**Live Demo**: [acme-sandbox.blinkpay.co.nz](https://acme-sandbox.blinkpay.co.nz)
A full-featured e-commerce checkout demo showcasing:
- **Gateway Flow** - Complete payment experience
- **Multiple Payment Amounts** - Test various transaction sizes
- **Real-time Status Updates** - See consent and payment state changes
- **Error Handling** - Examples of timeout, rejection scenarios
**How to Use:**
1. Select a product and amount
2. Click "Pay with BlinkPay"
3. Choose bank "PNZ" (sandbox bank)
4. Login with username `user01`, password `password`
5. Approve payment
6. Return to see payment confirmation
**Sandbox Testing**
This demo uses the BlinkPay sandbox environment. No real money is transferred, and you can test freely without consequences.
---
## Account Information Demo
### ACME Loans Demo
**Live Demo**: [acme-loans-dev.blinkpay.co.nz](https://acme-loans-dev.blinkpay.co.nz)
Demonstrates Blink Data (account information access):
- **Account Aggregation** - Connect multiple bank accounts
- **Balance Display** - View current and available balances
- **Transaction History** - Access historical transactions
- **Account Details** - See account metadata
**How to Use:**
1. Click "Connect Your Bank"
2. Select bank "PNZ"
3. Login with username `user01`, password `password`
4. Approve data access
5. View aggregated account information
---
## E-commerce Demo
### Snipcart Demo
**Blink Payments only.** An Astro shop that takes Blink PayNow at a Snipcart checkout through Snipcart's custom payment gateway. Netlify Functions on the server hold the client credentials and use the Node.js SDK to create a quick payment through the gateway flow, then check its outcome when the customer returns. It is a reference implementation, not a supported plugin; there are no plugins for platforms such as Shopify.
**Repository**: [github.com/BlinkPay/BlinkPay-Snipcart-Demo](https://github.com/BlinkPay/BlinkPay-Snipcart-Demo)
---
## Mobile Demo Application
### Flutter Demo App
A reference implementation for mobile integration on iOS and Android, useful whatever framework you build in. It is a demo app, not an SDK.
**Repository**: [github.com/BlinkPay/BlinkPay-Flutter-Demo](https://github.com/BlinkPay/BlinkPay-Flutter-Demo)
The app makes Blink Payments single and enduring consents through the Gateway flow. A small backend proxy (Dart Shelf, in `server/`) holds the client credentials and obtains access tokens, so the secret never reaches the app. The app opens the Gateway with `flutter_custom_tabs` (Chrome Custom Tabs on Android, SFSafariViewController on iOS) and the customer comes back through the custom-scheme return link `blinkpaydemo://callback`, received with `app_links`. The README's [deep linking setup](https://github.com/BlinkPay/BlinkPay-Flutter-Demo#deep-linking-setup) covers the platform configuration.
The demo takes shortcuts a production app should not: the app sets the amount and payment reference (your backend must), the app and backend share a fixed demo key instead of your own user authentication, and the consent ID is held only in memory rather than stored on your server. See [Mobile Integration](/docs/shared/flows/mobile-integration#reference-implementation).
---
## Testing Credentials
The ACME demos authorise with PNZ, BlinkPay's test bank, using these credentials. No real bank account is involved.
### Mock Bank (PNZ)
**Bank Name:** PNZ (the Gateway tile is labelled **PNZ**)
**Username:** `user01`
**Password:** `password`
**Decoupled auto-approve number:** `+64-259531933` (in the Gateway, choose **Send to PNZ app**)
**Test Scenarios:**
| Scenario | Steps | Result |
| --------------------- | ------------------------------------------------------ | ------ |
| Successful payment (Blink Payments) | Select PNZ β **Internet banking website** β Login with user01 β Approve | Consent `Authorised`, then the payment completes |
| Successful data access (Blink Data) | Start a bank connection β select PNZ β **Internet banking website** β Login with user01 β choose one or more accounts β Approve | Consent `Authorised`; the demo shows the accounts, balances and transactions shared |
| Decoupled Approval | Select PNZ β **Send to PNZ app** β enter `+64-259531933` | Consent approved automatically |
| Decline | Start authorisation β choose **Cancel** on the PNZ login page, before logging in | Consent `Rejected`; no payment is made and no account data is shared |
| Timeout | Start authorisation β don't complete it (or use any other mobile number for decoupled) β wait out the bank's window (up to 10 minutes) | Consent `Rejected` |
# Support & Help (https://merchants.blinkpay.co.nz/docs/shared/help/support.md)
---
title: Support & Help
description: Get help with your BlinkPay integration
---
# Support & Help
Need assistance with your BlinkPay integration? We're here to help!
## Contact Support
### Email Support
**Primary Contact:** [support@blinkpay.co.nz](mailto:support@blinkpay.co.nz)
**Response Times:**
- **Urgent Issues** (Production impacting): 24/7 support via email, or **Support** in the top navigation of the [Client Portal](https://merchants.blinkpay.co.nz/)
- **Standard Issues**: 2 business days for assignment
**When Contacting Support:**
Please include:
- Your client ID or company name
- Environment (sandbox or production)
- Description of the issue
- Steps to reproduce (if applicable)
- Error messages or codes
- The `request-id` response header of the failing call (Blink Payments)
- The consent ID and payment ID involved, if any
- Request/response examples (sanitise sensitive data, and never send your client secret or access tokens)
- Approximate time of occurrence
**Faster Resolutions**
The more details you provide, the faster we can help resolve your issue!
---
### General Inquiries
**Website:** [www.blinkpay.co.nz/support](https://www.blinkpay.co.nz/support)
For:
- Sales inquiries
- Partnership opportunities
- General information
- Product demos
---
## Self-Service Resources
### Documentation Portal
**API Documentation:** [merchants.blinkpay.co.nz](https://merchants.blinkpay.co.nz/)
Comprehensive API reference including:
- Complete endpoint documentation
- Request/response schemas
- Authentication details
- Error codes and descriptions
- OpenAPI specifications for [Blink Payments](https://merchants.blinkpay.co.nz/docs/debit/openapi.yaml) and [Blink Data](https://merchants.blinkpay.co.nz/docs/data/openapi.yaml)
### Integration Guides
Each product's section of the sidebar opens with its Overview and Quick Start, followed by its guides.
**Shared Guides:**
- [Authentication Guide](/docs/shared/authentication) - OAuth 2.0 setup
- [Going Live Checklist](/docs/shared/help/going-live) - Production readiness
---
## Sandbox Testing
**Endpoints:** `https://sandbox.debit.blinkpay.co.nz` (Blink Payments), `https://sandbox.data.blinkpay.co.nz` (Blink Data)
**Mock Bank:** PNZ (the Gateway tile is labelled **PNZ**)
**Test Credentials:** username `user01`, password `password`
**Decoupled:** mobile number `+64-259531933` auto-approves (in the Gateway, choose **Send to PNZ app**). Other numbers are accepted but never authorise: the consent stays `AwaitingAuthorisation` until the bank's window ends, then becomes `Rejected`.
**Decline:** choose **Cancel** on the PNZ login page, before logging in; the consent becomes `Rejected`.
**Revocation:** authorise a consent with the auto-approve number, then revoke it with the API's DELETE endpoint.
Nothing in the sandbox touches a real bank: no money moves and no real account data is shared.
---
## Community & Resources
### GitHub
**Organisation:** [github.com/BlinkPay](https://github.com/BlinkPay)
- SDK repositories (Blink Payments; Blink Data has no SDK and is called over HTTPS directly)
- Code examples
- Feature requests
### Postman Workspace
Each product has its own public Postman workspace with a collection, example requests and a sample environment. See [Postman Collection](/docs/shared/reference/postman) for both workspaces and how to set them up.
---
## Useful Links
### Documentation
- **API Docs**: [merchants.blinkpay.co.nz](https://merchants.blinkpay.co.nz/)
- **OpenAPI specifications**: [Blink Payments](https://merchants.blinkpay.co.nz/docs/debit/openapi.yaml) and [Blink Data](https://merchants.blinkpay.co.nz/docs/data/openapi.yaml)
- **Postman**: [Postman Collection](/docs/shared/reference/postman) (one workspace per product)
### Code & SDKs
- **GitHub**: [github.com/BlinkPay](https://github.com/BlinkPay)
### Demos
- **ACME Payments Demo** (Blink Payments): [acme-sandbox.blinkpay.co.nz](https://acme-sandbox.blinkpay.co.nz)
- **ACME Loans Demo** (Blink Data): [acme-loans-dev.blinkpay.co.nz](https://acme-loans-dev.blinkpay.co.nz)
- See [Demo Applications](/docs/shared/help/demos) for how to use them
### Contact
- **Support**: [support@blinkpay.co.nz](mailto:support@blinkpay.co.nz)
- **Website**: [www.blinkpay.co.nz](https://www.blinkpay.co.nz)