> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hifi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Transfer Approvals

> Transfer approvals provide multi-party authorization for transactions, adding security and compliance controls to your organization's transaction workflow. Require approval before executing high-value or sensitive transactions.

<Info>
  Transfer approvals apply to on-chain stablecoin transfers (wallet transfers,
  batch transfers, and bridging) and offramps. Onramps do not support approval
  workflows.
</Info>

## How Transfer Approvals Work

<Steps>
  <Step title="Create transfer with approval">
    Include `requireApproval: true` when creating a transfer via API.
  </Step>

  <Step title="Transfer enters pending state">
    Transfer status becomes `PENDING_APPROVAL` and does not execute.
  </Step>

  <Step title="Admin notification">
    Dashboard admins receive email notifications about the pending approval.
  </Step>

  <Step title="Approve or reject">
    Authorized admins review and either approve or reject the transfer.
  </Step>

  <Step title="Execution or cancellation">
    If approved, transfer executes normally. If rejected, transfer is cancelled.
  </Step>
</Steps>

## Implementation Options

Transfer approvals work differently depending on where transactions are initiated:

### Dashboard Approvals

When transfers are created in the HIFI Dashboard:

* **Members** can initiate wallet transfers
* **Member-initiated transfers** require Admin approval before execution
* **Admin-initiated transfers** execute without approval
* **Members** can initiate offramp transactions, and these will **always** enter the transfer approval flow (require Admin approval)
* **Email notifications** sent for approvals and rejections

### API Approvals

When using the API:

* Include `requireApproval: true` when creating transfers
* Transfers enter approval workflow until approved or rejected
* Supported on wallet transfers, batch transfers, bridging, and offramps
* **Offramp transactions** will enter the transfer approval flow **only** if `requireApproval: true` is set in the request body
* Can be integrated into your application's approval logic

## Creating Transfers with Approval

Add the `requireApproval` parameter to any supported transaction endpoint.

**Request:**

```bash theme={null}
curl -X POST "https://sandbox.hifi.com/v2/wallets/transfers" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "a40ea2aa-7937-4be9-bb1f-b75f1489bcc6",
    "source": {
      "userId": "usr_abc123"
    },
    "destination": {
      "userId": "usr_xyz789"
    },
    "amount": 10000,
    "currency": "usdc",
    "chain": "POLYGON",
    "requireApproval": true
  }'
```

**Response:**

```json theme={null}
{
  "transferType": "WALLET.TRANSFER",
  "transferDetails": {
    "id": "xfr_abc123",
    "requestId": "a40ea2aa-7937-4be9-bb1f-b75f1489bcc6",
    "status": "PENDING_APPROVAL",
    "amount": 10000,
    "currency": "usdc",
    "chain": "POLYGON",
    "receipt": {
      "approval": {
        "id": "apv_xyz789",
        "status": "PENDING",
        "transferId": "xfr_abc123",
        "transferType": "WALLET.TRANSFER",
        "createdAt": "2025-02-03T16:11:36.654998+00:00",
        "votes": []
      }
    }
  }
}
```

### Supported Endpoints

The `requireApproval` parameter works with:

| Endpoint                             | Transfer Type                          |
| :----------------------------------- | :------------------------------------- |
| `POST /v2/wallets/transfers`         | Single wallet transfers                |
| `POST /v2/wallets/transfers/batches` | Batch transfers (up to 50 recipients)  |
| `POST /v2/wallets/bridges`           | Cross-chain bridging transfers         |
| `POST /v2/offramps`                  | Offramp transfers (stablecoin to fiat) |

<Info>
  **Bridging and Offramp Note:** For bridging transfers and offramps, quotes are
  generated **after** approval. This prevents quotes from expiring while
  awaiting review.
</Info>

## Listing Pending Approvals

Retrieve all transactions awaiting approval using the [List Transaction Approvals](https://docs.hifi.com/api-reference/transfer-approval/list-transfer-approvals) endpoint.

**Request:**

```bash theme={null}
curl -X GET "https://sandbox.hifi.com/v2/transfer-approvals?limit=20&offset=0" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Query Parameters:**

* **limit** (optional): Number of approvals to return (default: 20, max: 100)
* **offset** (optional): Number of approvals to skip for pagination (default: 0)

**Response:**

```json theme={null}
{
  "success": true,
  "message": "Pending approvals retrieved successfully",
  "count": 2,
  "approvals": [
    {
      "id": "apv_abc123",
      "transferId": "xfr_xyz789",
      "status": "PENDING",
      "transferType": "WALLET.TRANSFER",
      "createdAt": "2024-01-15T10:30:00.000Z",
      "updatedAt": "2024-01-15T11:00:00.000Z"
    },
    {
      "id": "apv_def456",
      "transferId": "bat_ghi789",
      "status": "PENDING",
      "transferType": "WALLET.TRANSFER.BATCH",
      "createdAt": "2024-01-15T09:15:00.000Z",
      "updatedAt": "2024-01-15T09:15:00.000Z"
    }
  ]
}
```

Use this endpoint to build approval dashboards or automate approval workflows.

## Approving Transfers

Approve pending transfers using the [Approve Transfer](https://docs.hifi.com/api-reference/transfer-approval/approve-transfer) endpoint.

**Request:**

```bash theme={null}
curl -X POST "https://sandbox.hifi.com/v2/transfer-approvals/apv_abc123/approve" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "comment": "Approved after reviewing transaction details"
  }'
```

**Request Fields:**

* **approvalId** (required, path): ID of the approval to approve
* **comment** (optional): Explanation for the approval decision (max 1000 characters)

**Response:**

```json theme={null}
{
  "success": true,
  "message": "Transfer approved and initiated successfully",
  "approvalId": "apv_abc123",
  "status": "APPROVED",
  "timestamp": "2024-01-15T11:00:00.000Z"
}
```

After approval, the transfer proceeds to execution immediately.

## Rejecting Transfers

Reject pending transfers using the [Reject Transfer](https://docs.hifi.com/api-reference/transfer-approval/reject-transfer) endpoint.

**Request:**

```bash theme={null}
curl -X POST "https://sandbox.hifi.com/v2/transfer-approvals/apv_abc123/reject" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "comment": "Transfer amount exceeds daily limit"
  }'
```

**Request Fields:**

* **approvalId** (required, path): ID of the approval to reject
* **comment** (optional): Reason for rejection (max 1000 characters)

**Response:**

```json theme={null}
{
  "success": true,
  "message": "Transfer rejected successfully",
  "approvalId": "apv_abc123",
  "status": "REJECTED",
  "timestamp": "2024-01-15T11:00:00.000Z"
}
```

Rejected transactions are cancelled and will not execute.

## Approval Status Flow

Transactions with `requireApproval: true` follow this status progression:

| Status                | Description                                      |
| :-------------------- | :----------------------------------------------- |
| **PENDING\_APPROVAL** | Transaction awaiting admin approval              |
| **APPROVED**          | Transaction approved and proceeding to execution |
| **REJECTED**          | Transaction rejected and cancelled               |

After approval, transactions move to normal execution statuses (`CREATED`, `INITIATED`, `PENDING`, `COMPLETED`).

<Warning>
  **Expiration Times:** Transactions in `PENDING_APPROVAL` status will
  automatically expire if not approved or rejected within the following
  timeframes: - **Onchain transfers** (wallet transfers, batch transfers,
  bridging): Expire after **1 week** - **Offramps**: Expire after **1 day**
  Expired transactions are automatically cancelled and cannot be executed.
</Warning>

<Note>
  **Status Updates:** Subscribe to `TRANSFER.APPROVAL.PENDING`,
  `TRANSFER.APPROVAL.APPROVED`, and `TRANSFER.APPROVAL.REJECTED` webhook events
  to receive real-time approval notifications. See [Webhooks](/docs/webhooks)
  for setup instructions.
</Note>

## Notifications

### Email Notifications

**Admins receive notifications when:**

* A transaction requires approval (Member-initiated)
* A transaction needs their review

**Members receive notifications when:**

* Their transaction request is approved
* Their transaction request is rejected (with reason if provided)

### Webhook Events

Subscribe to approval events for automated workflows:

| Event Type                     | Description                                   |
| :----------------------------- | :-------------------------------------------- |
| **TRANSFER.APPROVAL.PENDING**  | Transaction created, awaiting approval        |
| **TRANSFER.APPROVAL.APPROVED** | Transaction approved, proceeding to execution |
| **TRANSFER.APPROVAL.REJECTED** | Transaction rejected and cancelled            |

## Sample Code

Here's a complete approval workflow implementation:

<Steps>
  <Step title="Create a High-Value Transaction That Requires Approval">
    To trigger an approval workflow for transactions over a certain threshold (e.g., \$10k), include `requireApproval: true` in your request:

    ```javascript theme={null}
    async function createHighValueTransfer(amount, destinationUserId) {
      const requireApproval = amount > 10000;
      const transfer = await fetch('https://sandbox.hifi.com/v2/wallets/transfers', {
        method: 'POST',
        headers: {
          'Authorization': 'Bearer YOUR_API_KEY',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          requestId: crypto.randomUUID(),
          source: { userId: 'usr_treasury' },
          destination: { userId: destinationUserId },
          amount,
          currency: 'usdc',
          chain: 'POLYGON',
          requireApproval
        })
      }).then(r => r.json());

      if (transfer.transferDetails.status === 'PENDING_APPROVAL') {
        notifyApprovers(transfer.transferDetails.id);
      }
      return transfer;
    }
    ```

    If the transaction is flagged for approval, notify approvers (e.g., by email or internal notification).
  </Step>

  <Step title="List Pending Approvals (for Admin Review)">
    Fetch all pending transaction approvals to display in an admin dashboard:

    ```javascript theme={null}
    async function fetchPendingApprovals() {
      const approvals = await fetch(
        'https://sandbox.hifi.com/v2/transfer-approvals?limit=50',
        {
          headers: { 'Authorization': 'Bearer YOUR_API_KEY' }
        }
      ).then(r => r.json());
      
      return approvals.approvals.map(approval => ({
        id: approval.id,
        transferId: approval.transferId,
        type: approval.transferType,
        pendingSince: approval.createdAt
      }));
    }
    ```
  </Step>

  <Step title="Approve a Pending Transaction">
    Admins can approve a pending transaction by submitting an approval action:

    ```javascript theme={null}
    async function approveTransfer(approvalId, adminComment) {
      const approval = await fetch(
        `https://sandbox.hifi.com/v2/transfer-approvals/${approvalId}/approve`,
        {
          method: 'POST',
          headers: {
            'Authorization': 'Bearer YOUR_API_KEY',
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            comment: adminComment
          })
        }
      ).then(r => r.json());
      
      console.log('Transaction approved:', approval.approvalId);
      return approval;
    }
    ```
  </Step>

  <Step title="React to Approval Webhook Events">
    Use webhook events to automatically notify stakeholders and update your application's state:

    ```javascript theme={null}
    function handleApprovalWebhook(event) {
      switch (event.eventType) {
        case 'TRANSFER.APPROVAL.PENDING':
          sendSlackNotification({
            message: `Transaction ${event.data.transferId} requires approval`,
            amount: event.data.amount,
            destination: event.data.destination
          });
          break;
        case 'TRANSFER.APPROVAL.APPROVED':
          updateTransferStatus(event.data.transferId, 'APPROVED');
          notifyRequester(event.data.initiatorId, 'approved');
          break;
        case 'TRANSFER.APPROVAL.REJECTED':
          notifyRequester(event.data.initiatorId, 'rejected', event.data.comment);
          break;
      }
    }
    ```

    Subscribe to webhook endpoints to handle `TRANSFER.APPROVAL.*` events.
  </Step>
</Steps>

## Getting Help

* 📧 **Email:** [support@hifi.com](mailto:support@hifi.com)
* 💬 **Slack:** Message us in our shared Slack channel

## Related Resources

* [Wallet Transfers](/docs/transactions/transfers) - Single wallet transfers
* [Batch Transfers](/docs/features/batch-transfers) - Multiple recipient transfers
* [Bridging](/docs/transactions/bridging) - Cross-chain transfers
* [Webhooks](/docs/webhooks) - Real-time approval notifications
* [API Reference](https://docs.hifi.com/api-reference/transfer-approval/list-transfer-approvals) - Complete endpoint documentation
