---
id: asynchronous-operations
title: "Asynchronous operations"
sidebar_label: Asynchronous operations
hide_title: true
description: "Management API - How 202 Accepted, eventual consistency, and safe retries work"
---

import Ingress from '../../../src/components/ingress/ingress.react';

<Ingress title="Asynchronous operations">
    Every write to the Management API is accepted first and applied a moment later. Build for that
    and your integration stays correct under retries, timeouts, and race conditions.
</Ingress>

## What 202 Accepted means

Capture, refund, cancel, and reauthorize answer with `202 Accepted`, an empty body, and a
`Location` header pointing at the order. Reauthorizing to a higher amount is the one exception,
and it is covered [further down](#the-one-exception-reauthorizing-to-a-higher-amount).

```http
HTTP/1.1 202 Accepted
Location: /manage/orders/1b1f5ef6-92n1-4b46-b1dc-ae2e00c4c315
```

`202` means Walley has taken the request and will apply it. It does not mean the change is live.
For a few seconds afterward, Merchant Hub, settlement data, and this API can all still show the
order as it was before your call.

So do not treat a `202` as "the order now looks like X". Treat it as "the change is on its way".

:::warning Do not read back immediately
Fetching the order right after a `202` usually returns the old version. Use one of the two
patterns below instead.
:::

## Reading the result

### If you have a correlationId

When you have the action's `correlationId` from a webhook, pass it to
[Get order](/docs/manage/orders/get). Walley returns the order only once that action has been
applied, so you never read a stale version.

```bash
curl "https://api.uat.walleydev.com/manage/orders/{{orderId}}?correlationId={{correlationId}}" \
  -H "Authorization: Bearer {{accessToken}}"
```

Until the action lands, the call returns `404 Not Found`. Retry after at least 3 seconds.

### If you do not have a correlationId

Wait up to a minute before fetching, and keep the read off your customer's critical path. This
suits reporting jobs, nightly reconciliation, and anything else that is not time sensitive.

## Retrying safely

Retries are expected. Two rules keep them from doing damage.

**Send an idempotency key on every write.** Generate a UUID v4 per operation and send it as
`Walley-Idempotency`. If the response never arrives, repeat the exact same request with the same key
and Walley applies the operation once. See [Idempotency](/docs/manage/idempotency).

```http
Walley-Idempotency: 03304b06-cb33-4f78-bcea-86cb4b202ba0
```

**Know which failures are worth retrying.**

| Response | What happened                                                     | What to do                                                        |
| -------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- |
| `403`    | Missing permission, or the order has not finished syncing yet.    | Retry after at least 3 seconds. If it persists, check your access. |
| `404`    | Order not found, it has not finished syncing, or a `correlationId` you passed has not been applied yet. | Retry after at least 3 seconds.              |
| `409`    | A request with the same idempotency key is already running.       | Wait, then read the order back. Do not send a new key.            |
| `422`    | The request conflicts with the state of the order.                | Do not retry. Fix the request. See [Errors](/docs/manage/errors). |

A `403` or `404` right after an order was created is usually the sync delay, not a real problem.

## The one exception: reauthorizing to a higher amount

Raising an order's total needs a fresh credit check, and that can involve the customer. So
[Reauthorize order](/docs/manage/orders/reauthorize) answers `201 Created` instead of `202`, with
a `Location` header pointing at a status resource.

```http
HTTP/1.1 201 Created
Location: /manage/orders/b6be7ec0-311b-490d-8856-af8d00c783b5/reauthorize/1b1f5ef6-92n1-4b46-b1dc-ae2e00c4c315
```

Poll that URL until `status` is `Completed` or `Failed`.

```json
{
  "data": {
    "id": "ca81364e-895f-4250-bdb2-a9dafa13c4bc",
    "orderId": "b2be7nc0-351b-890d-8856-af8d00a783b5",
    "status": "Completed",
    "createdAt": "2023-01-17T12:09:27.029057+00:00"
  },
  "links": {},
  "metaData": {}
}
```

`Failed` means the credit check was declined or Walley needed more household income data. The
order keeps its original amount, so nothing is left half applied.

Lowering the amount or keeping it the same returns `202` like every other write.

## A checklist for your integration

- Treat `202` as accepted, not applied
- Send `Walley-Idempotency` on every capture, refund, cancel, and reauthorize
- Retry `403` and `404` after at least 3 seconds, never `422`
- Reuse the same idempotency key when you retry
- Poll the status URL when a reauthorize returns `201`
- Never block a customer-facing screen on a read-back

## Next steps

- [Order lifecycle](/docs/manage/concepts/order-lifecycle) covers which actions a status allows
- [Errors](/docs/manage/errors) lists the responses and what each one calls for
