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

# Mutations

> Create, update, and delete records via standard HTTP verbs. Includes bulk operations and update-by-filter.

The REST API uses standard HTTP verbs for writes: `POST` to create, `PATCH` to update, `DELETE` to delete. Updates follow [RFC 7396 JSON Merge Patch](https://www.rfc-editor.org/rfc/rfc7396) — only the fields you send are changed. The endpoints are auto-generated from your [Data Model](/features/backend/data-model/overview), so adding a field to a table extends the request and response schema automatically.

The examples assume a `students` table with fields like `firstName`, `email`, `age`, `isActive`, and a `city` relationship.

## Single record

### Create

`POST /api/rest/<table>` with a JSON body. Returns `201 Created` with the new record and a `Location` header.

```bash theme={null}
curl -X POST "https://your-gateway.example.com/gw/api/rest/students" \
  -H "Authorization: Bearer archie_YOUR_API_KEY" \
  -H "X-Project-ID: your-project-id" \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Alice",
    "email": "alice.jones@example.com",
    "age": 21,
    "isActive": true
  }'
```

```json theme={null}
{
  "data": {
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "firstName": "Alice",
    "email": "alice.jones@example.com",
    "age": 21,
    "isActive": true,
    "createdAt": "2025-12-15T14:30:00.000Z",
    "updatedAt": "2025-12-15T14:30:00.000Z"
  }
}
```

To skip the response body, send `Prefer: return=minimal` — you'll get `201 Created` with no body.

To link to a related record, include the foreign key in the input:

```json theme={null}
{
  "firstName": "Alice",
  "email": "alice.jones@example.com",
  "cityId": "e14638cb-6d72-4a36-b30f-9b763136a7bb"
}
```

For safe retries (network failures, timeouts), include an `Idempotency-Key` header — see [Idempotency](/features/backend/rest-api-explorer/idempotency).

### Update

`PATCH /api/rest/<table>/<id>` with the fields you want to change. Fields you don't include are left alone.

```bash theme={null}
curl -X PATCH "https://your-gateway.example.com/gw/api/rest/students/287cff0a-345b-4cca-9e9a-75a2161238fd" \
  -H "Authorization: Bearer archie_YOUR_API_KEY" \
  -H "X-Project-ID: your-project-id" \
  -H "Content-Type: application/merge-patch+json" \
  -d '{
    "age": 23,
    "isActive": false
  }'
```

```json theme={null}
{
  "data": {
    "id": "287cff0a-345b-4cca-9e9a-75a2161238fd",
    "firstName": "James",
    "age": 23,
    "isActive": false,
    "updatedAt": "2025-12-15T14:45:00.000Z"
  }
}
```

To explicitly null a field, include it with a `null` value:

```json theme={null}
{
  "bio": null,
  "middleName": null
}
```

Both `application/merge-patch+json` and `application/json` are accepted as `Content-Type`.

### Delete

`DELETE /api/rest/<table>/<id>`. Returns `204 No Content` on success.

```bash theme={null}
curl -X DELETE "https://your-gateway.example.com/gw/api/rest/students/287cff0a-345b-4cca-9e9a-75a2161238fd" \
  -H "Authorization: Bearer archie_YOUR_API_KEY" \
  -H "X-Project-ID: your-project-id"
```

## Bulk create

`POST /api/rest/<table>/_bulk` creates up to 100 records in one request.

```bash theme={null}
curl -X POST "https://your-gateway.example.com/gw/api/rest/students/_bulk" \
  -H "Authorization: Bearer archie_YOUR_API_KEY" \
  -H "X-Project-ID: your-project-id" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "firstName": "Alice", "email": "alice@example.com", "age": 21 },
      { "firstName": "Bob", "email": "bob@example.com", "age": 25 }
    ],
    "transactionMode": "ALL_OR_NOTHING"
  }'
```

### Transaction modes

| Mode                       | Behavior                                                               |
| -------------------------- | ---------------------------------------------------------------------- |
| `ALL_OR_NOTHING` (default) | Single transaction. Any failure rolls back the whole batch.            |
| `BEST_EFFORT`              | Independent inserts. Failures are reported but don't block other rows. |

Pass `transactionMode` in the body, or as a query parameter (`?mode=BEST_EFFORT`).

### Success — 201

```json theme={null}
{
  "data": {
    "success": true,
    "transactionMode": "ALL_OR_NOTHING",
    "totalRequested": 2,
    "totalSucceeded": 2,
    "totalFailed": 0,
    "items": [
      { "id": "f47ac10b-..." },
      { "id": "a1b2c3d4-..." }
    ],
    "errors": []
  }
}
```

### Partial success in BEST\_EFFORT — 207

```json theme={null}
{
  "data": {
    "success": false,
    "transactionMode": "BEST_EFFORT",
    "totalRequested": 3,
    "totalSucceeded": 2,
    "totalFailed": 1,
    "items": [
      { "id": "f47ac10b-..." },
      { "id": "c3d4e5f6-..." }
    ],
    "errors": [
      { "index": 1, "message": "Unique constraint violated: email already exists" }
    ]
  }
}
```

### Rollback in ALL\_OR\_NOTHING — 422

When any row fails in `ALL_OR_NOTHING` mode, the API returns `422` and `totalSucceeded` is `0` — none of the rows were inserted.

## Bulk delete

`DELETE /api/rest/<table>/_bulk` removes multiple records in one request.

### By ids (single primary key)

```bash theme={null}
curl -X DELETE "https://your-gateway.example.com/gw/api/rest/students/_bulk" \
  -H "Authorization: Bearer archie_YOUR_API_KEY" \
  -H "X-Project-ID: your-project-id" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": [
      "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
    ]
  }'
```

### By filters (composite primary keys)

```bash theme={null}
curl -X DELETE "https://your-gateway.example.com/gw/api/rest/orderProducts/_bulk" \
  -H "Authorization: Bearer archie_YOUR_API_KEY" \
  -H "X-Project-ID: your-project-id" \
  -H "Content-Type: application/json" \
  -d '{
    "filters": [
      { "orderId": "order-1", "productId": "prod-1" },
      { "orderId": "order-2", "productId": "prod-2" }
    ],
    "transactionMode": "ALL_OR_NOTHING"
  }'
```

You can't mix `ids` and `filters` in the same request. Composite primary keys require `filters`.

## Update by filter

`PATCH /api/rest/<table>` (no id in the path) updates **every record** matching the filter parameters. Useful for batch operations like "mark all expired".

```bash theme={null}
curl -X PATCH "https://your-gateway.example.com/gw/api/rest/students?isActive=equals.false&age=lt.18" \
  -H "Authorization: Bearer archie_YOUR_API_KEY" \
  -H "X-Project-ID: your-project-id" \
  -H "Content-Type: application/merge-patch+json" \
  -d '{
    "status": "inactive",
    "notes": "Auto-deactivated: underage"
  }'
```

<Warning>
  At least one filter parameter is **required**. A `PATCH` to `/<table>` with no filters returns `422 Unprocessable Entity` — this is intentional, to prevent accidentally updating every row in the table.
</Warning>

The filter syntax matches the [filtered list query](/features/backend/rest-api-explorer/queries#filtered-list).

## Limits

| Setting                     | Default |
| --------------------------- | ------- |
| Max items in a bulk request | 100     |
| Max field selection depth   | 3       |
| Max relations per query     | 10      |

Exceeding any limit returns `422 Unprocessable Entity` with details.

## Error responses

Errors follow the [Problem Details](https://www.rfc-editor.org/rfc/rfc9457) format with `Content-Type: application/problem+json`.

```json theme={null}
{
  "type": "/errors/validation-failed",
  "title": "Unprocessable Entity",
  "status": 422,
  "detail": "One or more fields failed validation",
  "instance": "/api/rest/students",
  "errors": [
    { "field": "email", "rule": "required", "message": "The field 'email' is required" }
  ]
}
```

For the full status-code reference and how to handle each one, see [Error handling](/features/backend/rest-api-explorer/error-handling).

## Permissions

Every mutation is checked against [Role-Based Access](/features/backend/app-services/role-based-access). Field-level write rules apply too — fields a role isn't allowed to write are rejected.

## FAQ

<AccordionGroup>
  <Accordion title="PATCH or PUT — which should I use?">
    Use `PATCH`. Archie's update endpoints implement RFC 7396 Merge Patch semantics — only the fields you send are changed. There's no full-record `PUT` replacement variant.
  </Accordion>

  <Accordion title="How do I retry a failed POST safely?">
    Send the same `Idempotency-Key` header on the retry. The API will return the cached response from the first call instead of creating a duplicate. See [Idempotency](/features/backend/rest-api-explorer/idempotency).
  </Accordion>

  <Accordion title="Why is bulk delete returning 207 Multi-Status?">
    `BEST_EFFORT` mode reports per-row outcomes — some succeeded, some didn't. The body's `errors` array tells you which rows failed and why. For all-or-nothing semantics, switch to `ALL_OR_NOTHING`.
  </Accordion>

  <Accordion title="Can I create a record with related rows in one call?">
    For nested writes — creating a parent and its children atomically — use the [GraphQL nested mutations](/features/backend/graphql-api-explorer/standard-operations#nested-mutations). REST creates flat records and links them via foreign-key columns.
  </Accordion>

  <Accordion title="What happens if I PATCH a field that doesn't exist?">
    The API returns `422 Unprocessable Entity` with the offending field name. Unknown fields are not silently ignored.
  </Accordion>
</AccordionGroup>
