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

# Update Transaction

> Partially update an existing transaction — merge custom metadata or deviceDetails, change channel or reason — without replacing the full record.

## Overview

Use this endpoint when you need to **enrich or correct** an existing transaction after creation: custom **metadata**, **deviceDetails**, **channel**, or **reason**. It does **not** change amount, status, parties, or other core fields (use [Change status](/en/use-cases/transaction-monitoring/change-status-api) for status).

<Note>
  **Shallow merge for `metadata` and `deviceDetails`:** only keys you send are overwritten; omitted keys are preserved. Nested objects (e.g. `metadata.tags`) are replaced as a whole when you send that key. To clear `channel`, send `"channel": null`.
</Note>

## Endpoints

| Method             | Endpoint                                    | Use when                                    |
| ------------------ | ------------------------------------------- | ------------------------------------------- |
| **By ID**          | `PATCH /transactions/{id}`                  | You have gu1’s transaction UUID             |
| **By external ID** | `PATCH /transactions/external/{externalId}` | You only have your `externalId` from create |

Both accept the same JSON body and return the same response shape.

```
PATCH https://api.gu1.ai/transactions/{id}
PATCH https://api.gu1.ai/transactions/external/{externalId}
```

## Authentication

Requires `transactions:edit` (API key or session with equivalent permission):

```bash theme={null}
Authorization: Bearer YOUR_API_KEY
```

## Query Parameters

<ParamField query="executeRules" type="boolean" default="false">
  When `true`, re-runs KYT rules with trigger `transaction_updated` after the patch (same mode as status change). Default: `false`.
</ParamField>

## Request Body

At least **one** field is required.

<ParamField body="metadata" type="object">
  Shallow merge into existing `metadata`. Example: existing `{ "purpose": "payroll", "tags": { "a": 1 } }` + body `{ "tags": { "b": 2 } }` → `{ "purpose": "payroll", "tags": { "b": 2 } }` (top-level `tags` is replaced as a whole).
</ParamField>

<ParamField body="deviceDetails" type="object">
  Shallow merge into existing `deviceDetails` (stored in `device_details`). Use this to add or correct device context after create — e.g. `ipAddress`, `deviceId`, `osName`, `manufacturer`, `model`, fraud flags (`isVpn`, `isEmulator`, …). Same field schema as [Create transaction](/en/api-reference/transactions/create) (`deviceDetails`). Rules evaluate paths like `deviceDetails.ipAddress`.
</ParamField>

<ParamField body="channel" type="string | null">
  Transaction channel (max 50 chars), e.g. `web`, `mobile`, `api`. Send `null` to clear.
</ParamField>

<ParamField body="reason" type="string">
  Outcome / decline reason enum. See [Reason enum](/en/api-reference/transactions/reason-enum).
</ParamField>

### Example

```json theme={null}
{
  "metadata": {
    "clientReference": "INV-2026-0042",
    "tags": { "segment": "retail" }
  },
  "deviceDetails": {
    "ipAddress": "203.0.113.10",
    "deviceId": "dev-abc-123",
    "osName": "iOS",
    "osVersion": "17.0",
    "manufacturer": "Apple",
    "model": "iPhone"
  },
  "channel": "mobile",
  "reason": "FRAUD_SUSPECTED"
}
```

## Response (200 OK)

```json theme={null}
{
  "success": true,
  "transaction": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "externalId": "tx_12345",
    "channel": "mobile",
    "reason": "FRAUD_SUSPECTED",
    "metadata": {
      "purpose": "payroll",
      "clientReference": "INV-2026-0042",
      "tags": { "segment": "retail" }
    },
    "deviceDetails": {
      "ipAddress": "203.0.113.10",
      "deviceId": "dev-abc-123",
      "osName": "iOS"
    },
    "updatedAt": "2026-05-29T12:00:00.000Z"
  }
}
```

When `executeRules=true`, **`rulesExecutionSummary`** is included (same shape as create / change status).

## Errors

| Status | Code               | When                                 |
| ------ | ------------------ | ------------------------------------ |
| 400    | `VALIDATION_ERROR` | Invalid body or no effective change  |
| 400    | `NO_CHANGES`       | Values match what is already stored  |
| 404    | `NOT_FOUND`        | Transaction not in your organization |
| 403    | —                  | Missing `transactions:edit`          |

## Side effects

* **Audit:** `transaction_updated` event on the transaction timeline with changed fields (`metadata`, `deviceDetails`, `channel`, and/or `reason` in `changes`).
* **Webhook:** `transaction.updated` (includes `changes` and current `deviceDetails` when applicable).
* **Rules:** only when `executeRules=true`.

## Examples

```bash theme={null}
curl -X PATCH "https://api.gu1.ai/transactions/external/tx_12345" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "metadata": { "note": "Corrected after bank callback" },
    "channel": "api"
  }'
```

## Related

<CardGroup cols={2}>
  <Card title="Get transaction" icon="receipt" href="/en/api-reference/transactions/get">
    Read full transaction after update
  </Card>

  <Card title="Change status" icon="rotate" href="/en/use-cases/transaction-monitoring/change-status-api">
    Update lifecycle status and re-run rules
  </Card>

  <Card title="Create transaction" icon="plus" href="/en/api-reference/transactions/create">
    Initial metadata on create
  </Card>

  <Card title="Reason enum" icon="list" href="/en/api-reference/transactions/reason-enum">
    Allowed `reason` values
  </Card>
</CardGroup>
