# Transactions

Source: https://developers.swell.is/backend-api/transactions

Use `POST /:transaction` to run a group of write operations as one atomic unit—either every operation commits, or none of them do. Use a transaction when a partial result would leave your data in an inconsistent state: creating an order and decrementing stock, moving a balance between two accounts, or writing a record and its child records together.

### Making a request

Send an array of operations. They run in order, in a single database transaction.

```javascript
await swell.post('/:transaction', [
  { method: 'post', url: '/products', data: { name: 'Chair', price: 40 } },
  { method: 'put', url: '/products/6812a1f4c0', data: { stock_level: 12 } },
  { method: 'delete', url: '/products/6812a1f4c1' },
]);
```

Each operation takes the following fields:

- `url` (required): The endpoint the operation writes to.
- `method` (optional): `post`, `put`, or `delete`. Defaults to the method of the transaction request itself.
- `data` (optional): The body for that operation.

Reads are not allowed inside a transaction—a `get` operation is rejected before anything runs. Each operation is permission-checked on its own, the same as if you had sent it as a standalone request.

### Scoping to one collection

Put the collection in the transaction URL and the operations inherit it:

```javascript
await swell.post('/:transaction/products', [
  { method: 'post', data: { name: 'Chair', price: 40 } },
  { method: 'put', url: '6812a1f4c0', data: { stock_level: 12 } },
]);
```

### Response

On success, the response is an array in the same order as the operations you sent. Each entry is what that operation would have returned on its own:

```json
[
  { "id": "6812a1f4c2", "name": "Chair", "price": 40 },
  { "id": "6812a1f4c0", "name": "Desk", "stock_level": 12 }
]
```

### Limits

- Operations per transaction: 10
- Methods allowed: `post`, `put`, `delete`
- Time per operation: 2 seconds
- Time to commit: 2 seconds
- Concurrent transactions per store: 10
- API quota cost: 1 per operation, plus 1 for the transaction

Operations run one after another, not in parallel. Keep transactions small: they hold locks on every record they touch, so a long transaction makes conflicts more likely for everyone writing to the same records.

### Errors

A failed transaction rolls back completely and returns a typed error:

```json
{
  "error": {
    "message": "Transaction exceeded time budget",
    "code": "transaction_timeout",
    "status": 408
  }
}
```

- `transaction_conflict` (409): Another write touched the same records first. Retry with backoff.
- `transaction_throttled` (429): Too many transactions in flight for your store. Retry with backoff.
- `transaction_timeout` (408): An operation or the commit ran past its time budget. Not retryable.
- `transaction_op_failed` (the operation's own status): One operation was rejected. Fix the operation and send it again.
- `transaction_error` (400 for an invalid request body, 503 otherwise): The request body was rejected, or the database was unreachable.

When an operation fails, the error carries `op_index`, the zero-based position of the operation that caused the rollback:

```json
{
  "error": {
    "message": "Product not found",
    "code": "transaction_op_failed",
    "status": 404,
    "op_index": 1
  }
}
```

Retry `transaction_conflict` and `transaction_throttled` with exponential backoff. The other errors will fail the same way again.

### Events and webhooks

Nothing is dispatched while a transaction is running. Webhooks and app functions make HTTP calls that can't be rolled back, so they are held back until the transaction is known to have committed.

- No per-record events (such as `product.created`) are written for operations inside a transaction.
- One `transaction.committed` event fires after a successful commit, listing every operation in the transaction.
- If the transaction rolls back, nothing fires at all.

Subscribe to `transaction.committed` in your app's [webhooks](https://developers.swell.is/apps/webhooks) or [event-triggered functions](https://developers.swell.is/apps/functions) when you need to react to the group as a whole.

### Debugging

Every transaction writes a log entry with its outcome, operation count, and duration. View them with the [Swell CLI](https://developers.swell.is/apps/cli):

```bash
swell logs --type transaction
```
