> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://api.docs.modulards.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://api.docs.modulards.com/_mcp/server.

# Writes and asynchronous operations

## Create, update and delete

| Operation | Answer                                                                              |
| --------- | ----------------------------------------------------------------------------------- |
| Create    | `201 Created` with the new resource document and a `Location` header pointing at it |
| Update    | `200 OK` with the updated resource document                                         |
| Delete    | `204 No Content`, with no body                                                      |

Write requests send a JSON body and need a read and write token (see [Authentication](/authentication)).

## Asynchronous operations

Some operations reach the WordPress installation of each website, or copy data, and take seconds to minutes. They answer `202 Accepted` as soon as the work is queued:

| Operation                                                              | Creates                  |
| ---------------------------------------------------------------------- | ------------------------ |
| Updater actions (install, upgrade, activate and deactivate, uninstall) | `site-actions`           |
| Backups (create and retry)                                             | `site-backups`           |
| Malware scans                                                          | `site-scans`             |
| Broken Links Checker scans                                             | `site-broken-link-scans` |

The `202` document lists the resources created, one per website that accepted the work, and the websites that were skipped:

```json
{
  "data": [
    { "type": "site-actions", "id": "9181", "attributes": { "status": "pending" } }
  ],
  "meta": {
    "skipped": [
      {
        "site_id": 42,
        "site_name": "Shop",
        "site_action_id": null,
        "reason_code": "SITE_UNREACHABLE",
        "message": "This website is not connected; connect it first."
      }
    ]
  },
  "jsonapi": { "version": "1.1" }
}
```

* `data` can be empty: when every website was skipped, the answer is still `202`.
* Each entry of `meta.skipped` names the website (`site_id`, `site_name`), carries the id of the related target when there is one, a machine-readable `reason_code` and a readable `message`. `reason_code` is one of `SITE_UNREACHABLE`, `UNSUPPORTED`, `NOT_FOUND`, `PERMISSION_DENIED`, `CONFLICT`, `ALREADY_APPLIED` and `QUOTA_EXCEEDED`; new codes can be added (see [Versioning](/versioning)).
* `meta.warnings` appears when the work was queued with something you should know about.

`202` means queued, not done. Follow each created resource by its id until it settles.

### Following a site action

Read the action with its `show` endpoint and watch `status`:

| `status`          | Meaning                                                                                                                                     |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `pending`         | Queued, not started                                                                                                                         |
| `in_progress`     | Running on the website                                                                                                                      |
| `requires_action` | Waiting for approval: approve it or roll it back with the validate endpoint under [Website Actions](/modular-ds-public-api/website-actions) |
| `done`            | Finished successfully                                                                                                                       |
| `failed`          | Finished with an error                                                                                                                      |

### Following a backup

A backup copy takes minutes. Read it, or list backups filtered by `phase`, and watch `phase`:

| `phase`       | Meaning                                                                        |
| ------------- | ------------------------------------------------------------------------------ |
| `in_progress` | The copy is running                                                            |
| `done`        | The copy finished                                                              |
| `failed`      | The copy failed                                                                |
| `excluded`    | Only for a part of a backup that had nothing to copy, never for a whole backup |

Restorations can be read through the API, but they are started from the dashboard.

## Polling

Poll gently: a few seconds between reads of the same resource is enough, and every read counts towards your [rate limit](/rate-limits).