Quick start
This page takes you from getting your keys to your first received payment. The samples use the api() helper from Authentication and request signing.
1. Get your API key and secret
Section titled “1. Get your API key and secret”-
Sign in to the FIN console at https://portal.finixpay.app with the partner owner account.
-
Open Account settings. The API keys section shows your API key (it starts with
pk_live_, or is 32 hex characters for older accounts). Copy it. -
Click Create a new secret, confirm, and copy the secret that is shown. It is shown only once, and any previous secret stops working immediately.
-
Store both values in your server’s environment variables or secret manager.
Terminal window export PARTNER_API_KEY="pk_live_xxxxxxxxxxxxxxxxxxxxxxxx"export PARTNER_API_SECRET="<the secret you copied>"
2. Make your first call
Section titled “2. Make your first call”Call GET /v1/account to check that your signing works, then GET /v1/banks to get the supported bank codes.
import { api } from './partner-api.mjs';
const { data: account } = await api('GET', '/v1/account');console.log(account.status, account.balance.available);
const { data: banks } = await api('GET', '/v1/banks');console.log(banks.map((b) => b.code)); // cache these codesrequire 'partner_api.php';
$account = partner_api('GET', '/v1/account')['data'];echo $account['status'], ' ', $account['balance']['available'], PHP_EOL;
$banks = partner_api('GET', '/v1/banks')['data'];print_r(array_column($banks, 'code')); // cache these codesfrom partner_api import api
account = api("GET", "/v1/account")["data"]print(account["status"], account["balance"]["available"])
banks = api("GET", "/v1/banks")["data"]print([b["code"] for b in banks]) # cache these codesExample response of GET /v1/account:
{ "data": { "object": "account", "name": "Example Shop", "status": "active", "payinsEnabled": true, "payoutsEnabled": true, "ipAllowListEnabled": false, "limits": { "payinMinimum": "100.00", "payinMaximum": "50000.00", "payoutMinimum": "100.00", "payoutMaximum": "50000.00", "dailyWithdrawalLimit": "0.00" }, "balance": { "currency": "THB", "total": "125000.00", "available": "120000.00" }, "createdAt": "2026-01-01T00:00:00.000Z" }, "requestId": "req_Z3k9QmP2xR7tLw4NbV8cYh1s"}If you get a 401, see Common signing mistakes.
3. Create a pay-in
Section titled “3. Create a pay-in”One call is enough: send the payer’s bank account inline in payer, together with the amount, your order number in reference and the URL that receives webhooks in webhookUrl. The payer (your end user’s bank account) is created the first time and reused automatically for later pay-ins and payouts with the same bankCode and accountNumber.
const { data: payin } = await api('POST', '/v1/payins', { payer: { bankCode: 'SCB', accountNumber: '1234567890', accountName: 'สมชาย สบายดี', }, amount: '150.00', currency: 'THB', reference: 'order-10001', webhookUrl: 'https://shop.example.com/webhooks/payments', returnUrl: 'https://shop.example.com/orders/10001',});// save payin.id with the order (and payin.payerId with your user, if you like)$payin = partner_api('POST', '/v1/payins', [ 'payer' => [ 'bankCode' => 'SCB', 'accountNumber' => '1234567890', 'accountName' => 'สมชาย สบายดี', ], 'amount' => '150.00', 'currency' => 'THB', 'reference' => 'order-10001', 'webhookUrl' => 'https://shop.example.com/webhooks/payments', 'returnUrl' => 'https://shop.example.com/orders/10001',])['data'];payin = api("POST", "/v1/payins", { "payer": { "bankCode": "SCB", "accountNumber": "1234567890", "accountName": "สมชาย สบายดี", }, "amount": "150.00", "currency": "THB", "reference": "order-10001", "webhookUrl": "https://shop.example.com/webhooks/payments", "returnUrl": "https://shop.example.com/orders/10001",})["data"]The important parts of the response:
{ "object": "payin", "id": "pin_3f2b1c0d9e8f4a7b8c6d5e4f3a2b1c0d", "reference": "order-10001", "payerId": "payer_7c0a4b8e2f3d4a1b9c8d7e6f5a4b3c2d", "requestedAmount": "150.00", "amount": "150.37", "currency": "THB", "method": "promptpay_qr", "status": "pending", "checkoutUrl": "https://pay.example.com/3f2b1c0d-9e8f-4a7b-8c6d-5e4f3a2b1c0d", "payTo": { "bankCode": "KBANK", "bankName": "Kasikorn Bank", "accountNumber": "0987654321", "accountName": "Example Co., Ltd.", "promptpayId": "0812345678", "qrPayload": "00020101021229370016A000000677010111011300668123456785802TH53037645406150.3763046572" }}If the payer already existed under a different name, the stored name is kept (it is used to match the transfer) and the response has a warnings entry payer_name_differs. See Inline payer.
4. Show the payment to the payer
Section titled “4. Show the payment to the payer”Choose one option:
- Hosted payment page: redirect the payer to
checkoutUrl. When they are done, the page sends them back toreturnUrl. - Your own page: render a QR code from
payTo.qrPayloadwith any QR library, and show the destination account frompayTo. Forbank_transferpay-insqrPayloadisnull; show the account only. - Ready-made image: create or retrieve the pay-in with
?include=paymentImage(for examplePOST /v1/payins?include=paymentImage, signed with that query string) to also getpaymentImage, a PNG data URI for an<img src="...">.
5. Receive the webhook
Section titled “5. Receive the webhook”When the transfer is matched, the platform sends a payin.succeeded event with POST to your webhookUrl. A minimal handler is below; see Webhooks for full signature verification.
// Expressapp.post('/webhooks/payments', express.raw({ type: 'application/json' }), async (req, res) => { if (!verifyWebhook(req.get('X-Webhook-Signature'), req.body)) return res.status(400).end(); const event = JSON.parse(req.body); if (event.type === 'payin.succeeded') { await markOrderPaid(event.data.reference, event.data.id); // must be idempotent } res.status(200).end(); // reply quickly, do slow work afterwards});6. Reconcile when needed
Section titled “6. Reconcile when needed”Webhooks are not retried automatically. If your endpoint was down or you are unsure, call GET /v1/payins/{id} for the current status, or search by order number with GET /v1/payins?reference=order-10001.
Next steps
Section titled “Next steps”- Fields and statuses of Pay-ins and Payouts
- Managing Payers directly
- Testing and go-live checklist