> This page is for Unified.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.merge.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.merge.dev/_mcp/server.

# Async operations

> How to use Merge's async write endpoints to create records without blocking. Covers polling for completion and handling async response statuses.

> **Info**
>
> Asynchronous POST requests are currently only supported in Merge's [Accounting](/merge-unified/accounting/overview) category. This feature is currently in beta. Reach out to your account representative or [contact us](mailto:support@merge.dev) for more information.

---

## Singular asynchronous requests

To make a singular asynchronous request, append **/async** at the end of the respective synchronous POST endpoint. For asynchronous requests, the server returns a `task_id`, a unique identifier used to track the status of the async operation.

### Endpoint

**POST** `/<common_model>/async`

### Sample response

```json
{
    "task_id": "dc27d1b7-6627-4f2e-8b0e-0439f76642ff"
}
```

To check the progress and get the final result, send a request to the task polling endpoint:

[**GET https://api.merge.dev/api/accounting/async-tasks/\{task\_id}**](/merge-unified/accounting/data-management/async-tasks/retrieve).

### Task statuses

| Status        | Meaning                                                                                  |
| ------------- | ---------------------------------------------------------------------------------------- |
| `QUEUED`      | The task has been accepted and has not started running against the third party           |
| `IN_PROGRESS` | The task is running                                                                      |
| `COMPLETED`   | The task finished, and `result` carries the created or updated object                    |
| `FAILURE`     | The task finished with an error, and `result` carries the status code and the error body |

Keep polling until the task reports `COMPLETED` or `FAILURE`, the only two terminal statuses. A task reports `QUEUED` for as long as it takes Merge to schedule the write, which can be several seconds after you receive the `task_id`, so treat `QUEUED` as normal rather than as a sign that the `task_id` is wrong.

---

## Bulk asynchronous requests

For high-volume write operations, certain Common Models support the Bulk POST feature, which allows you to create or update multiple objects in a single API call rather than making separate requests for each one. Use this instead of singular async requests when you need to write many objects at once, since it minimizes API overhead and helps you stay within third-party rate limits.

### Integration support

The bulk write endpoint currently supports the following integrations and Common Models. If you have specific Common Models or integrations you'd like to see coverage for, please let us know by reaching out to your CSM or [support@merge.dev](mailto:support@merge.dev)!

| **Integration**   | **Common Models**                         |
| ----------------- | ----------------------------------------- |
| NetSuite          | Invoices, Item Fulfillments, Sales Orders |
| Quickbooks Online | Expenses, Invoices                        |
| Xero              | Expenses                                  |

### Endpoint

**POST** `/<common_model>/bulk`

### Sample request body

The request body must adhere to the following requirements:

* Max payload size of 5 MB or 100 objects
* Each object must contain a unique `item_id` that maps to the item's ID in the third-party system
* Each object should be identical in structure to the payload used in the single-object POST endpoint for the corresponding Common Model

```json
{
    "batch_items": [
        {
            "item_id": "1",
            "payload": {
              "type": "ACCOUNTS_RECEIVABLE",
              "contact": "022a2bef-57e5-4def-8ed2-7c41bd9a5ed8",
              "number": "AIQ12546",
              "issue_date": "2020-03-31T00:00:00Z",
              "due_date": "2020-04-15T00:00:00Z",
              "paid_on_date": "2020-04-01T00:00:00Z",
              "memo": "Weekly Payment",
              "company": "595c8f97-2ac4-45b7-b000-41bdf43240b5",
              "employee": "7442f0d5-722d-45bd-b807-6e38489d37fe",
              "currency": "USD",
              "exchange_rate": "2.9",
              "payment_term": "89d329de-825f-4ac6-8369-3c58b4e68bee",
              "total_discount": 0,
              "sub_total": 100,
              "status": "DRAFT",
              "total_tax_amount": 5,
              "total_amount": 105,
              "balance": 105,
              "tracking_categories": [
                "7dc5ca17-d311-44cd-9ce0-333080367a18"
              ],
              "accounting_period": "7dc5ca17-d311-44cd-9ce0-333080367a18",
              "line_items": [
                {
                  "description": "Pickleball lessons",
                  "unit_price": 50,
                  "quantity": 1,
                  "total_amount": 50,
                  "currency": "USD",
                  "exchange_rate": "2.9",
                  "employee": "7442f0d5-722d-45bd-b807-6e38489d37fe",
                  "tax_rate": "a12e7c20-1922-9df7-s75n-edfeewnn7384",
                  "item": "5b3c1341-a20f-4e51-b72c-f3830a16c97b",
                  "account": "cd0f32d4-a493-11ec-b909-0242ac120002",
                  "project": "22e65a5d-2df5-4e6e-884a-e538d0339000",
                  "contact": "908934-49j9-093f-0989-908923908",
                  "tracking_categories": [
                    "b38c59b0-a9d7-4740-b1ee-5436c6751e3d"
                  ],
                  "company": "595c8f97-2ac4-45b7-b000-41bdf43240b5"
                }
              ],
              "inclusive_of_tax": true
            }
        }
    ]
}
```

### Sample response

```json
{
    "batch_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```

To check the status or error messages returned from the 3rd party integration, utilize the batch polling endpoint:

**GET** `/<common_model>/bulk/{batch_id}`

### Sample polling response

```json
{
  "batch_id": "123e4567-e89b-12d3-a456-426614174000",
  "status": "PARTIAL_SUCCESS",
  "total_count": 2,
  "objects": [
    {
      "item_id": "1",
      "status": "SUCCESS",
      "response": {
        "merge_common_model_id": "9871b4a9-f5d2-4f3b-a66b-dfedbed42c46"
      }
    },
    {
      "item_id": "2",
      "status": "FAILED",
      "response": {
        "error_message": "You cannot update this invoice at this time"
      }
    }
  ]
}
```

### Batch statuses

| Status            | Meaning                                                                                                             |
| ----------------- | ------------------------------------------------------------------------------------------------------------------- |
| `ENQUEUED`        | The request has been received and a task has been enqueued for processing                                           |
| `IN_PROGRESS`     | The enqueued task is being processed                                                                                |
| `PARTIAL_SUCCESS` | The task has been processed, but not all objects were written successfully                                          |
| `SUCCESS`         | The task has been processed, and all objects were written successfully                                              |
| `FAILED`          | The task has been processed, but ran into an error while processing                                                 |
| `RATE_LIMITED`    | The request was received but ran into rate limits while processing. Rate-limited objects are automatically retried. |

### Per-object statuses

| Status    | Meaning                                 |
| --------- | --------------------------------------- |
| `PENDING` | This object has not been processed yet  |
| `SUCCESS` | This object was successfully POSTed     |
| `FAILURE` | This object was not successfully POSTed |

---

## Integration-specific behavior

For integration-specific behavior, data, and best practices, visit this [help center article](https://app.usepylon.com/kb/631a0357-3fc2-407a-a85c-ba81fa95ccd0/articles/6f884e09-f676-4c96-9443-89f9c758ba30).

---

## Webhooks

Instead of polling the async task status endpoints, you can subscribe to webhook events that fire automatically when async operations complete:

* **AsyncPost.completed** fires when a single async POST request reaches a terminal state (COMPLETED or FAILURE).
* **AsyncBulkPost.completed** fires when a bulk async POST batch reaches a terminal state (SUCCESS, PARTIAL\_SUCCESS, or FAILED).

For full payload examples and instructions, see [Merge Webhooks](/merge-unified/reading-data/webhooks/merge-webhooks#webhook-event-types).