---
title: "Wallet operation completed or failed"
method: POST
path: "wallet-operation"
tags: ["Webhooks"]
---

# Wallet operation completed or failed

`POST wallet-operation` (webhook)

Webhook that is called when an asynchronous embedded-wallet operation reaches a terminal state. Fires `WALLET_OPERATION.COMPLETED` on terminal success and `WALLET_OPERATION.FAILED` on terminal failure. The payload is self-contained — handle it from the fields below with no follow-up API call needed.

The specific operation is carried in `data.operationType` (`auth_credential.create`, `auth_credential.delete`, `session.revoke`, or `wallet.export`), and the business resource it affected is carried in `data.resourceType` / `data.resourceId`:

| `operationType` | `resourceType` | `resourceId` identifies |
| --- | --- | --- |
| `auth_credential.create` | `AUTH_METHOD` | the created credential — its id can't be known before this webhook, since the create request has nothing to echo |
| `auth_credential.delete` | `AUTH_METHOD` | the deleted credential |
| `session.revoke` | `SESSION` | the revoked session |
| `wallet.export` | `INTERNAL_ACCOUNT` | the account whose wallet was exported |

The webhook carries no sensitive result material — for `wallet.export`, the export bundle itself is never delivered here; retrieve it by resubmitting the original signed export request until it returns the result.

### Correlating this webhook

- **`data.requestId`** is the primary correlation key. It is the same `Request-Id` value you supplied on the signed retry that produced this terminal result — echo it against the `Request-Id` you sent (and, if you polled through one or more `200 { status: "PROCESSING" }` responses, against the value you kept resending).
- **`data.resourceId`** is the primary correlator for the *business resource* itself, distinct from `requestId`'s role of matching the request. For `auth_credential.create` this is the only way to learn the created credential's id — the request that created it had nothing to echo.
- **`id`** (the top-level webhook envelope id) is for deduplication. Grid may redeliver a webhook after a transient failure; track `id` to avoid double-processing.
- **`data.operationId`** is a Grid-internal identifier for the operation. It's useful when contacting support about a specific operation, but isn't intended as a correlation key.

This endpoint should be implemented by clients of the Grid API.

### Authentication

The webhook includes a signature in the `X-Grid-Signature` header that allows you to verify that the webhook was sent by Grid. To verify the signature:

1. Get the Grid public key provided to you during integration
2. Decode the base64 signature from the header
3. Create a SHA-256 hash of the request body
4. Verify the signature using the public key and the hash

If the signature verification succeeds, the webhook is authentic. If not, it should be rejected.

## Payload

- WalletOperationWebhook
  - `id` string, required — Unique identifier for this webhook delivery (can be used for idempotency)
  - `type` 'WALLET_OPERATION.COMPLETED' | 'WALLET_OPERATION.FAILED', required — Type of webhook event in OBJECT.EVENT dot-notation. The part before the dot identifies the resource, the part after identifies the event. This lets consumers route purely on type without inspecting data.status.
  - `timestamp` string, date-time, required — ISO 8601 timestamp of when the webhook was sent
  - `data` union, required
    - WalletOperationCompletedData
      - `operationId` string, required — Grid-internal identifier for this operation. Useful when contacting support about a specific operation; not a correlation key — use `requestId` to match this webhook to the request you made.
      - `requestId` string, required — The `Request-Id` you supplied on the signed retry that produced this terminal result — the same value you would have echoed on every retry had you received a `200 { status: "PROCESSING" }` response while it was settling. This is the primary way to correlate this webhook to the request you made.
      - `operationType` 'auth_credential.create' | 'auth_credential.delete' | 'session.revoke' | 'wallet.export', required — The kind of operation that reached a terminal state.
      - `resourceType` 'AUTH_METHOD' | 'SESSION' | 'INTERNAL_ACCOUNT', required — The kind of business resource `resourceId` identifies. Determined by `operationType`: `auth_credential.create` and `auth_credential.delete` → `AUTH_METHOD`, `session.revoke` → `SESSION`, `wallet.export` → `INTERNAL_ACCOUNT`.
      - `resourceId` string, required — LSID of the business resource this operation affected. For `auth_credential.create`, this is the **primary way to learn the new credential's id** — the request that created it can't have supplied one in advance. For `auth_credential.delete` and `session.revoke` it echoes the `AuthMethod:<uuid>` / `Session:<uuid>` you already knew and referenced in the request; for `wallet.export` it's the `InternalAccount:<uuid>` whose wallet was exported.
      - `status` 'completed', required — Terminal status of the operation.
    - WalletOperationFailedData
      - `operationId` string, required — Grid-internal identifier for this operation. Useful when contacting support about a specific operation; not a correlation key — use `requestId` to match this webhook to the request you made.
      - `requestId` string, required — The `Request-Id` you supplied on the signed retry that produced this terminal result — the same value you would have echoed on every retry had you received a `200 { status: "PROCESSING" }` response while it was settling. This is the primary way to correlate this webhook to the request you made.
      - `operationType` 'auth_credential.create' | 'auth_credential.delete' | 'session.revoke' | 'wallet.export', required — The kind of operation that reached a terminal state.
      - `resourceType` 'AUTH_METHOD' | 'SESSION' | 'INTERNAL_ACCOUNT', required — The kind of business resource `resourceId` identifies. Determined by `operationType`: `auth_credential.create` and `auth_credential.delete` → `AUTH_METHOD`, `session.revoke` → `SESSION`, `wallet.export` → `INTERNAL_ACCOUNT`.
      - `resourceId` string, required — LSID of the business resource this operation affected. For `auth_credential.create`, this is the **primary way to learn the new credential's id** — the request that created it can't have supplied one in advance. For `auth_credential.delete` and `session.revoke` it echoes the `AuthMethod:<uuid>` / `Session:<uuid>` you already knew and referenced in the request; for `wallet.export` it's the `InternalAccount:<uuid>` whose wallet was exported.
      - `status` 'failed', required — Terminal status of the operation.
      - `error` OperationError, required — Failure details for a terminally failed operation.
        - `code` string, required — Machine-readable failure code for a `FAILED` operation. Codes are Grid-defined and stable regardless of which vendor Grid uses under the hood for a given operation.

## Acknowledgement `200`

Webhook received successfully

## Other responses

- `400` — Bad request
- `401` — Unauthorized - Signature validation failed
- `409` — Conflict - Webhook has already been processed (duplicate id)

---

[API](https://skmtc.dev/stainless-api/apis/grid-api.md) · [All operations](https://skmtc.dev/stainless-api/apis/grid-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/stainless-api/grid-api/revisions/2ec84c06fbec/schema)
