# Oxyforge agent API

Create a temporary WordPress website for the person you are helping, build it with WP-CLI, and
hand it over with a claim link. No account or API key is needed to start.

Base URL: https://api.oxyforge.com/agent/v1

Prefer MCP? The same tools are an MCP server (streamable HTTP, no auth) at https://api.oxyforge.com/mcp.

## Rules

- Every site is password protected. Visitors (and you) need the visitor login to see it. This cannot be turned off.
- A site is up for 24 hours. If nobody has claimed it by then, it is deleted. A claimed site sleeps for 24 hours first, so its owner can choose a plan.
- Only the person can keep a site: they open the claim link and sign up free. Claiming a sleeping site wakes it once.
- Sites cannot send email, and until claimed can only reach the web (HTTP/HTTPS) from inside.
- Until claimed, every page shows an Oxyforge bar along the bottom with the claim link and the time left before the site expires. It is for the person, you cannot remove it, and it goes away by itself when they claim.
- A site gets a quarter of a CPU core. One kept busy at its limit for 15 minutes is put to sleep early.
- Custom themes and plugins are welcome: write files (section 3b) or install a zip.

## 1. Create a site

```
POST /agent/v1/sites
Content-Type: application/json

{"name": "bakery", "title": "Ann's Bakery", "client": "Claude"}
```

All fields are optional. `sitePassword` (8+ characters) sets the visitor password; otherwise one is made up. If the person gave you an Oxyforge affiliate code, send it as `ref`: the claim link then carries it, so their sign-up is credited to that affiliate.

The answer (202) has `site.token`. **Save it: it is shown once** and is the only way to work on the
site. Send it on every other call as `Authorization: Bearer oxa_...`. It also has `site.url`,
`site.visitorLogin`, `site.wordpress` (wp-admin login) and `site.claimUrl`.

## 2. Wait until it is awake

`GET /agent/v1/sites/{name}` every 10 seconds until `site.state` is `awake` (about a minute).

States: provisioning, awake, going_to_sleep, asleep, waking, failed, deleting, deleted.

## 3. Build it

```
POST /agent/v1/sites/{name}/wp
{"command": "post create --post_type=page --post_title='About Us' --post_status=publish --porcelain"}
```

Runs one WP-CLI command (without the leading `wp`). Or send `{"args": ["post", "create", ...]}` to
skip quoting. Long content: put it in `stdin` and use `-` as the file, e.g.
`{"command": "post create - --post_type=page --post_title=Home --post_status=publish --porcelain", "stdin": "<!-- wp:paragraph --><p>Hello</p><!-- /wp:paragraph -->"}`.

The answer has `ok`, `exitCode`, `stdout`, `stderr`. One command at a time per site.

Useful commands:
- `theme install twentytwentyfive --activate`
- `post create --post_type=page --post_title=Contact --post_status=publish --porcelain`
- `post update 12 --post_content='...'`, `post list --post_type=page --fields=ID,post_title,post_name`
- `option update show_on_front page` then `option update page_on_front 12`
- `option update blogname "Ann's Bakery"`, `option update blogdescription "Fresh every morning"`
- `menu create Main`, `menu item add-post main 12`, `menu location assign main primary`
- `plugin install contact-form-7 --activate`
- `media import https://example.com/photo.jpg --title=Shopfront --porcelain`
- `rewrite structure '/%postname%/'`

Not available: `eval`, `eval-file`, `shell`, `db cli/import/export`, `package`, `--exec`, `--require`.
`plugin install` and `theme install` take a wordpress.org slug, an https:// link to a zip, or a zip you
uploaded, e.g. `theme install wp-content/uploads/bakery.zip --activate`.

## 3b. Write your own theme or plugin

Files live under wp-content, in `themes/`, `plugins/` or `uploads/` only. Folders are made for you.

```
PUT /agent/v1/sites/{name}/files
{"path": "themes/bakery/style.css", "content": "/*\nTheme Name: Bakery\n*/\nbody{font-family:serif}"}
```

Binary files: send `contentBase64` instead of `content` (25 MB at most). Then activate it with
`theme activate bakery` or `plugin activate my-plugin`.

- `GET /agent/v1/sites/{name}/files?path=themes/bakery` lists a folder (a path without a file extension);
  `?path=themes/bakery/style.css` reads a file.
- `DELETE /agent/v1/sites/{name}/files?path=themes/bakery/old.css` removes one.

Other calls:
- `POST /agent/v1/sites/{name}/login-link`: one-use wp-admin link, 5 minutes.
- `POST /agent/v1/sites/{name}/clear-cache`: if a change does not show.
- `DELETE /agent/v1/sites/{name}`: throw an unclaimed site away.

## 4. Hand it over

Tell the person:
1. The site address (`site.url`) and the visitor login (`site.visitorLogin`).
2. The claim link (`site.claimUrl`): open it, sign up free or log in, and the site moves into their
   Oxyforge account. Do this within 24 hours: an unclaimed site is deleted when its time runs out.
3. After claiming, they can keep it for good by choosing a plan.

If they let you keep access when claiming, your token keeps working. If they turn it off, calls answer 403.

## Working on a person's own Oxyforge sites

An MCP client can instead connect to https://api.oxyforge.com/mcp/account and sign in with Oxyforge (OAuth 2.1, PKCE, dynamic client registration); the access token it gets is an account agent key. If the person gives you an account agent key (oxk_...), send it as the bearer token. `GET /agent/v1/sites`
lists the sites it covers; every call above then works on those sites by name, plus
`POST /agent/v1/sites/{name}/backup` before a risky change. A read-only key can look but not change: it may run WP-CLI commands that only read (`post list`, `option get`, `plugin list`, `theme status`, `core version`), never `config` or `db`.
Keys never delete sites or touch domains, billing or the team.

## Limits

Sites per network per day and sites at once are capped. A 429 or 503 says when to try again.
