# ⭐️ Local Payout API Integration Guide

This guide explains the complete integration flow for sending local payouts using the Pivot Payment API. It covers authentication, beneficiary validation, payout creation, webhook handling, and payout status reconciliation.

This page provides an end-to-end workflow. For full endpoint specifications, refer to the individual API references.

### Overview

Pivot Local Payout API allows merchants to transfer funds from their Pivot balance to beneficiary bank accounts in Indonesia.

A typical payout integration consists of the following steps:

1. Authenticate API requests
2. Check available payout balance
3. Validate beneficiary bank account (Inquiry Account)
4. Create payout transaction
5. Receive payout status via webhook callback
6. Retrieve payout status for reconciliation (optional)

***

### Prerequisites

Before integrating the payout API, ensure that:

* Your merchant account has been activated for **Local** **Payout**.
* You have received the following credentials from Pivot:

| Credential    | Description                        |
| ------------- | ---------------------------------- |
| Client Key    | Used to authenticate requests      |
| Client Secret | Used to sign requests              |
| Merchant ID   | Unique identifier for your account |

You should also configure:

* A **secure webhook endpoint** to receive payout callbacks
* Proper **idempotency handling** to avoid duplicate payouts

***

### Base URL

**Production**

```
https://api.pivot-payment.com
```

**Sandbox**

```
https://api-stg.pivot-payment.com
```

***

### Authentication

All API requests must include authentication headers. A valid access token must be included in the `Authorization` header when sending requests to the Pivot API.

Endpoint reference:

[**Authentication**](/pivot-docs/api-references/api-information/authentication.md)

Example headers:

```
X-MERCHANT-ID: [Your Client ID]
X-MERCHANT-SECRET: [Your Client Secret]
```

Example request:

```
{
    "grantType": "client_credentials"
}
```

Example response:

```
{
    "code": "00",
    "message": "Success",
    "data": {
        "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJiYWNrZW5kLXBvcnRhbCIsInN1YiI6IjkyMmUzOWFiLTc1NjUtNDlmNi1iODRmLWZiNTYxMjI4MjFhZSIsImV4cCI6MTcxNDAyODE0MywiY2xpZW50SWQiOiI5MjJlMzlhYi03NTY1LTQ5ZjYtYjg0Zi1mYjU2MTIyODIxYWUiLCJtZXJjaGFudElkIjoiOTIyZTM5YWItNzU2NS00OWY2LWI4NGYtZmI1NjEyMjgyMWFlIn0.EkxckAJEcB4fgVU97mQC5eooBwQ7vhexzksafyUgOPU",
        "expiresIn": "900",
        "tokenType": "Bearer"
    }
}
```

Access tokens expire after **900 seconds (15 minutes)**. You must request a new access token once the current token expires.

***

### Recommended Integration Flow

The recommended payout flow is shown below.

```
Merchant System
      │
      │
      ├── Check Balance
      │
      ├── Inquiry Account (validate beneficiary)
      │
      ├── Create Payout
      │
      ├── Receive Webhook Callback
      │
      └── Retrieve Payout Status (optional)
```

***

### Step 1 — Check Payout Balance

Before creating a payout, ensure that your Pivot balance is sufficient to cover the transaction amount. If the balance is insufficient, the payout request will not be processed. The transaction will appear in the dashboard under **Local** **Payout → Need Action → Waiting for Top Up**, where you may **retry** the payout after topping up your balance or **cancel** the transaction.

Endpoint reference:

[**Payout Balance**](https://pivot-payment.gitbook.io/pivot-docs/api-references/~/changes/62/api-lists/payout-local/payout-balance)

Example request:

```
GET /v1/payouts/balance?currency=IDR
```

Example response:

```
{
  "code": "00",
  "message": "Success",
  "data": {
    "availableBalance": {
      "currency": "IDR",
      "value": "4440916697.16"
    }
  }
}
```

***

### Step 2 — Validate Beneficiary Account

Before creating a payout, validate the beneficiary account using **Inquiry Account**.

This ensures:

* the bank account exists
* the account name matches the expected beneficiary

Endpoint reference:

[**Inquiry Account**](https://pivot-payment.gitbook.io/pivot-docs/api-references/~/changes/62/api-lists/payout-local/inquiry-account)

Example request:

```
{
  "channelCode": "BCA",
  "channelInformation": {
    "accountNumber": "999966660001",
    "accountName": "Reforza Pivot"
  }
}
```

Example response:

```
{
  "account_name": "BUDI SANTOSO",
  "bank_code": "014",
  "account_number": "1234567890",
  "status": "VALID"
}
```

You should display the returned **account\_name** to your user for confirmation.

***

### Step 3 — Create Payout

Once the beneficiary is validated, create the payout transaction. You have two options to create a payout:

* Using inquiryId, or
* Using channel information

Endpoint reference:

[**Create Payout**](/pivot-docs/api-references/api-lists/payout-local/create-a-payout.md)

Example request using inquiryId:

```
{
  "payouts": [
    {
      "referenceId": "1999",
      "inquiryId": "d6a46a2c-1b26-40c5-9ca3-e063e69635a0",
      "amount": {
        "value": "100000",
        "currency": "IDR"
      },
      "description": "test Reforza Pivot"
    }
  ]
}
```

Example request using channel information:

```
{
  "payouts": [
    {
      "referenceId": "999",
      "channelCode": "BRI",
      "channelInformation": {
        "accountNumber": "888801000157508",
        "accountName": "Reforza Pivot"
      },
      "amount": {
        "value": "100000",
        "currency": "IDR"
      },
      "description": "test Reforza Pivot"
    }
  ]
}
```

*Note: For the full list of channel codes, refer to* [**Channel Codes**](/pivot-docs/api-references/api-lists/payout-local/channel-codes.md).

Example response:

```
{
  "code": "00",
  "message": "Success",
  "data": {
    "created": "2024-05-13T08:21:44.967496435Z",
    "merchantId": "922e39ab-7565-49f6-b84f-fb56122821ae",
    "payouts": [
      {
        "amount": {
          "currency": "IDR",
          "value": "100000"
        },
        "inquiryId": "",
        "channelCode": "BRI",
        "channelInformation": {
          "accountName": "Reforza Pivot",
          "accountNumber": "888801000157508"
        },
        "description": "test Reforza Pivot",
        "referenceId": "999"
      }
    ],
    "status": "IN_PROGRESS",
    "updated": "2024-05-13T08:21:44.967496538Z",
    "uuid": "d6a46a2c-1b26-40c5-9ca3-e063e69635a0"
  }
}
```

Important notes:

* `reference_id` must be **unique** for each payout.
* Use **idempotency** to prevent duplicate transactions.

***

### Step 4 — Handle Webhook Callback

Pivot sends a webhook callback when the payout status is updated.

Typical payout statuses:

| Status     | Meaning                                          |
| ---------- | ------------------------------------------------ |
| PROCESSING | Payout is being processed                        |
| SUCCESS    | Funds successfully transferred                   |
| FAILED     | Payout failed                                    |
| DELAYED    | Payout is taking longer than expected to process |

Example webhook payload for bulk payout:

```
{
  "event": "PAYOUT.DONE",
  "data": {
    "uuid": "d6a46a2c-1b26-40c5-9ca3-e063e69635a0",
    "merchantId": "922e39ab-7565-49f6-b84f-fb56122821ae",
    "payoutResults": {
      "totalPendingCount": 0,
      "totalPendingAmount": 0,
      "totalSuccessCount": 1,
      "totalSuccessAmount": 100000,
      "totalFailedCount": 0,
      "totalFailedAmount": 0,
      "totalCancelledCount": 0,
      "totalCancelledAmount": 0
    },
    "status": "DONE"
  }
}
```

Example webhook payload for single payout:

```
{
  "event": "PAYOUT.SUCCESS",
  "data": {
    "uuid": "d6a46a2c-1b26-40c5-9ca3-e063e69635a0",
    "merchantId": "922e39ab-7565-49f6-b84f-fb56122821ae",
    "payouts": {
      "referenceId": "TestReforzaPivot001",
      "amount": {
        "currency": "IDR",
        "value": "100000"
      },
      "status": "SUCCESS",
      "reason": ""
    }
  }
}
```

Your webhook endpoint should:

1. Verify the callback authenticity
2. Update payout status in your system
3. Return HTTP 200 to acknowledge receipt

Example response:

```
HTTP 200 OK
```

If your endpoint returns an error or timeout, Pivot may retry sending the webhook. For more details on the retry mechanism, refer to [**Callback**](/pivot-docs/api-references/api-information/callback.md).

***

### Step 5 — Retrieve Payout Status (Optional)

You may retrieve payout status via API for reconciliation or if webhook delivery fails.

Endpoint reference:

[**Retrieve Payout**](/pivot-docs/api-references/api-lists/payout-local/retrieve-a-payout.md)

Example request:

```
GET /v1/payouts/{uuid}
```

Example response:

```
{
  "code": "00",
  "message": "Success",
  "data": {
    "created": "2024-05-13T08:21:45Z",
    "merchantId": "922e39ab-7565-49f6-b84f-fb56122821ae",
    "payoutResults": {
      "totalFailedAmount": 0,
      "totalFailedCount": 0,
      "totalPendingAmount": 0,
      "totalPendingCount": 0,
      "totalSuccessAmount": 100000,
      "totalSuccessCount": 1,
      "totalCancelledCount": 0,
      "totalCancelledAmount": 0
    },
    "payouts": [
      {
        "amount": {
          "currency": "IDR",
          "value": "100000"
        },
        "channelCode": "BRI",
        "channelInformation": {
          "accountName": "Reforza Pivot",
          "accountNumber": "888801000157508"
        },
        "created": "2024-05-13T08:21:45Z",
        "inquiryId": "d6a46a2c-1b26-40c5-9ca3-e063e69635a0",
        "description": "test Reforza Pivot",
        "referenceId": "999",
        "status": "SUCCESS",
        "reason": "",
        "updated": "2024-05-13T08:21:46Z"
      }
    ],
    "status": "DONE",
    "updated": "2024-05-13T08:21:46Z",
    "uuid": "d6a46a2c-1b26-40c5-9ca3-e063e69635a0"
  },
  "pagination": {
    "page": 1,
    "perPage": 20,
    "totalItems": 1,
    "totalPages": 1
  }
}
```

To retrieve the details of a specific payout, refer to [**Retrieve a Single Payout Request**](/pivot-docs/api-references/api-lists/payout-local/retrieve-a-single-payout-request.md)**.**

Example request:

```
GET /v1/payouts/:{uuid}?referenceId=:{referenceId}
```

Example response:

```
{
  "code": "00",
  "message": "Success",
  "data": {
    "merchantId": "922e39ab-7565-49f6-b84f-fb56122821ae",
    "payoutResults": {
      "totalFailedAmount": 0,
      "totalFailedCount": 0,
      "totalPendingAmount": 0,
      "totalPendingCount": 0,
      "totalSuccessAmount": 100000,
      "totalSuccessCount": 1,
      "totalCancelledCount": 0,
      "totalCancelledAmount": 0
    },
    "payouts": {
      "amount": {
        "currency": "IDR",
        "value": "100000"
      },
      "channelCode": "BRI",
      "channelInformation": {
        "accountName": "Reforza Pivot",
        "accountNumber": "888801000157508"
      },
      "created": "2024-05-13T08:21:45Z",
      "inquiryId": "d6a46a2c-1b26-40c5-9ca3-e063e69635a0",
      "description": "test Reforza Pivot",
      "referenceId": "999",
      "status": "SUCCESS",
      "reason": "",
      "updated": "2024-05-13T08:21:46Z"
    },
    "uuid": "d6a46a2c-1b26-40c5-9ca3-e063e69635a0"
  }
}
```

***

### Idempotency

To prevent duplicate transaction processing, all payout requests must include the `X-REQUEST-ID` header. Pivot uses this value to ensure that the same request is not processed multiple times.

If the same `X-REQUEST-ID` is received more than once within the validity period, Pivot will treat the request as the **same transaction** and return the original response instead of creating a new payout.

#### X-REQUEST-ID Requirements

The `X-REQUEST-ID` value must meet the following requirements:

* Must contain **only alphanumeric characters**
* **Minimum length:** 16 characters
* **Maximum length:** 36 characters
* The ID remains **valid for 24 hours**

After **24 hours**, the same `X-REQUEST-ID` may be reused for a new transaction request.

Example Header:

```
X-REQUEST-ID: a1b2c3d4e5f6g7h8
```

#### Tips

Further measure to prevent duplicate payouts:

* Do not retry payouts blindly if the transaction has not reached final status
* Always check payout status before retrying

***

### Error Handling

Common payout failure scenarios include:

| Error                             | Cause                                                   |
| --------------------------------- | ------------------------------------------------------- |
| Invalid account                   | Beneficiary account is invalid or doesn't exist         |
| Declined due to beneficiary limit | Beneficiary account has already reached the daily limit |
| Failed to process by Bank Network | Bank network failed to process the transaction          |

Refer to the [**Response Codes and Failure Reasons**](/pivot-docs/api-references/api-lists/payout-local/response-codes-and-failure-reason.md) section for full error mappings.

***

### Common Integration Questions

#### What happens if the webhook fails?

If your webhook endpoint fails to respond with HTTP 200, Pivot will retry sending the callback.\
You should ensure your endpoint is highly available.

***

#### Can I retry a payout?

Do not retry immediately.\
Always retrieve the payout status first to avoid duplicate transfers.

***

#### Should I rely on webhook or API status?

Webhook is the **primary mechanism** for status updates.\
Retrieve payout status only for reconciliation or webhook failures.

***

#### Do I need to perform Inquiry Account before every payout?

Yes. This ensures the beneficiary account is valid and prevents payout failures.

***

#### How do I avoid duplicate payouts?

Use a unique `reference_id` for each payout and implement idempotency logic in your system.

***

### API References

For full endpoint specifications, refer to:

* Payout Balance
* Inquiry Account
* Create Payout
* Retrieve Payout
* Callback
* Response Codes and Failure Reasons
* Test Scenarios
* Channel Codes


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://pivot-payment.gitbook.io/pivot-docs/api-references/api-lists/payout-local/local-payout-api-integration-guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
