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

# Accounts

> Provision and manage customer accounts: create idempotently, set plans, grant credits, change status, and read connected sites.

A managed account is a customer account you provision and own. This guide covers
the full lifecycle. Every call needs the `provision` scope except the reads, which
need `read`.

## Provision an account

`POST /accounts` creates a customer account keyed by your own `externalRef`.

```bash theme={null}
curl -X POST https://api.wpcursor.com/api/partner/v1/accounts \
  -H "X-Partner-Key: $WPOS_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "owner@customer.com", "externalRef": "ws_123", "name": "Jordan Lee", "company": "Customer Co", "planId": "starter" }'
```

| Field         | Required | Notes                                                                          |
| ------------- | -------- | ------------------------------------------------------------------------------ |
| `email`       | yes      | The account owner's email.                                                     |
| `externalRef` | yes      | Your own id for this customer. Provisioning is idempotent on it.               |
| `name`        | no       | Person or account name.                                                        |
| `company`     | no       | Company name.                                                                  |
| `planId`      | no       | Defaults to your partner default plan. An unknown plan returns `plan_invalid`. |

<Note>
  **Idempotent by design.** Two calls with the same `externalRef` return the same
  account. The first returns `201` with `existing: false`; a repeat returns `200`
  with `existing: true` and ignores the other body fields. Build your provisioning
  as fire-and-forget: retries never create duplicates.
</Note>

The account is created with no password. Your customer never logs into WPOS
directly; they activate the plugin with the returned `licenseKey`, and you drive
everything else through the API or the [dashboard](/partners/dashboard).

<Warning>
  If the email already belongs to an account that is not yours, the call returns
  `409 email_conflict` and never attaches to it. Use an email your customer
  controls, or one namespaced to your platform.
</Warning>

## Read an account

`GET /accounts/:accountId` returns the account with its credit balances and site
count.

```json theme={null}
{
  "success": true,
  "account": {
    "id": "acc_...",
    "email": "owner@customer.com",
    "plan": "starter",
    "status": "active",
    "externalRef": "ws_123",
    "credits": { "balance": 480, "usedTotal": 20, "addonBalance": 0 },
    "siteCount": 1,
    "createdAt": "2026-08-08T..."
  }
}
```

## Change the plan

`POST /accounts/:accountId/plan` moves an account to a different plan.

```bash theme={null}
curl -X POST https://api.wpcursor.com/api/partner/v1/accounts/acc_.../plan \
  -H "X-Partner-Key: $WPOS_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "planId": "pro" }'
```

## Grant credits

`POST /accounts/:accountId/credits` adds credits to an account. This is a grant
from your partner allocation, not a purchase: the customer gets no billing email.

```bash theme={null}
curl -X POST https://api.wpcursor.com/api/partner/v1/accounts/acc_.../credits \
  -H "X-Partner-Key: $WPOS_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 500, "reason": "Monthly top-up" }'
```

`amount` is a positive integer. The response returns the new balance. Watch for
the [`credits.low` webhook](/partners/webhooks#events) to top accounts up before
they run dry.

## Suspend or reactivate

`POST /accounts/:accountId/status` sets the account's status. Suspending stops the
account from spending credits within one plugin validation cycle (about five
minutes); setting it back to `active` re-enables it.

```bash theme={null}
curl -X POST https://api.wpcursor.com/api/partner/v1/accounts/acc_.../status \
  -H "X-Partner-Key: $WPOS_PARTNER_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "suspended" }'
```

`status` is one of `active`, `suspended`, or `cancelled`. Use this when a customer
churns or fails to pay you: it is your lever, not WPOS's.

## List an account's sites

`GET /accounts/:accountId/sites` returns the WordPress sites connected to the
account, including each site's white-label handoff state.

```json theme={null}
{
  "success": true,
  "sites": [
    {
      "id": "site_...",
      "siteUrl": "https://customer.com",
      "siteTitle": "Customer Co",
      "pluginVersion": "2.3.0",
      "isActive": true,
      "lastSeenAt": "2026-08-08T...",
      "handoff": { "mode": true, "agencyName": "Acme Agency", "logoUrl": "https://...", "primaryColor": "#1B3A6B", "showChat": true }
    }
  ]
}
```

<Note>
  You do not create sites by API. A site appears here once your customer activates
  the plugin on a real WordPress site with the account's license key. That
  registration also fires a [`site.registered` webhook](/partners/webhooks#events).
</Note>

## Ownership and errors

Every account and site must resolve to your partner. A request for one that is not
yours returns `404 not_found`, never `403`, so the API never leaks whether an id
exists. See the [error reference](/partners/api-reference#errors).

## Next

<CardGroup cols={2}>
  <Card title="Build jobs" icon="robot" href="/partners/build-jobs">
    Run the agent on a connected site.
  </Card>

  <Card title="Branding" icon="paintbrush" href="/partners/branding">
    White-label the assistant on a site.
  </Card>
</CardGroup>
