[MCP server](https://to.co/en-ca/docs/mcp?format=md) · **API**

# The to.co API

Let your own software, such as a website, a till system or a script, read your company on to.co and keep it up to date. Keys are on Pro and Grow.

- Base URL: `https://api.to.co/v1/orgs/{host}`
- Authentication: `Authorization: Bearer toco_ak_…`

## Get an API key

Open your company in my.to.co and choose API keys, then Create key. Name it, choose its scopes and when it expires (a year at most), and copy it: it is shown once and only a hash of it is kept.

Send the key in the Authorization header on every request. A key works for its own company only: any other address answers not found. The API takes keys only, never a browser session.

Example call:

```shell
curl https://api.to.co/v1/orgs/mybusiness.uk.to.co \
  -H "Authorization: Bearer toco_ak_…"
```

## Scopes and plans

A key can do only what its scopes allow, and only what the company's plan includes at the moment of each request. Reading is on Pro and Grow; changes are on Grow.

| Scope | What it does | From plan |
|---|---|---|
| `company:read` | Your company details, services and locations. | Pro |
| `messages:read` | Messages from your contact form (not those held as spam). | Pro |
| `company:write` | Replace your services, and change phone numbers and opening hours. | Grow |
| `posts:read` | The posts you write on to.co, drafts included. | Pro |
| `posts:write` | Write, save, publish, unpublish and delete posts. | Grow |

## Routes

Every route is under the base URL, with your company's to.co address in place of `{host}`, for example mybusiness.uk.to.co. Requests and answers are JSON. Changes go through the same checks, history and republishing as changes made in my.to.co, recorded as made by the key.

| Route | What it does | Scope |
|---|---|---|
| `GET /v1/orgs/{host}` | The company record, with its services and locations. | `company:read` |
| `GET /v1/orgs/{host}/services` | The services, in order. | `company:read` |
| `GET /v1/orgs/{host}/locations` | The locations, with phones and opening hours. | `company:read` |
| `GET /v1/orgs/{host}/messages` | Contact form messages. Filter with ?status=new\|read\|replied\|archived. | `messages:read` |
| `GET /v1/orgs/{host}/messages/{id}` | One message. | `messages:read` |
| `PATCH /v1/orgs/{host}` | Change the company phone numbers: JSON { "phones": … }. | `company:write` |
| `PUT /v1/orgs/{host}/services` | Replace the whole list of services, in order: JSON { "services": [ … ] }. | `company:write` |
| `PATCH /v1/orgs/{host}/locations/{location}` | Change a location: JSON with any of phones, normal_hours and special_hours. Every value is checked, and the answer carries the checks. The registered office comes from the register and cannot be changed. | `company:write` |
| `GET /v1/orgs/{host}/posts` | Posts written on to.co, newest change first, a page at a time: ?limit, ?state and ?cursor (next_cursor from the page before). | `posts:read` |
| `GET /v1/orgs/{host}/posts/{id}` | One post, its body as blocks, with its version as the ETag. | `posts:read` |
| `POST /v1/orgs/{host}/posts` | A new draft: JSON { "title", "standfirst", "body" }, or the body as text/markdown. | `posts:write` |
| `PATCH /v1/orgs/{host}/posts/{id}` | Save a post. Send the version you are changing as If-Match; JSON, or the body as text/markdown. | `posts:write` |
| `DELETE /v1/orgs/{host}/posts/{id}` | Move a post to the bin. The owner can restore it in my.to.co for 30 days. | `posts:write` |
| `POST /v1/orgs/{host}/posts/{id}/publish` | Publish the If-Match version. It is checked first, as when the owner publishes. | `posts:write` |
| `POST /v1/orgs/{host}/posts/{id}/unpublish` | Take a post off the page and back to a draft. | `posts:write` |

## Credits and limits

- Each request uses one API credit, from the same monthly allowance as your own AI assistant's calls to your business's MCP server: 5,000 a month per company on Pro and 50,000 on Grow. A request that fails gives its credit back. Credits start again on the 1st of each month (UTC), and there are no overage charges. Answers carry X-Credits-Limit, X-Credits-Remaining and X-Credits-Reset.
- Up to 120 requests a minute per key.
- Up to 60 changes a minute per company, counted with changes made in my.to.co and over MCP.

## Errors

An error answers with an HTTP status and JSON: error (a message), code (one of those below) and request_id. A 429 carries Retry-After.

- `invalid_request` (400): The body or a query value is not valid; the message says which field.
- `cookie_not_accepted` (400): The request carried a browser session. This API takes an API key only.
- `if_match_required` (400): A post change without If-Match.
- `not_ready` (400): The post cannot be published yet; problems lists why.
- `image_unknown` (400): A post body shows an image the post does not have. A key cannot upload images.
- `invalid_key` (401): No key, or one that is unknown, revoked or expired.
- `insufficient_scope` (403): The key does not have the scope this route needs.
- `plan_required` (403): The company plan does not include this scope now; plan_needed names the plan that does.
- `insufficient_role` (403): The person who made the key no longer has the role for this; an owner makes a new key.
- `not_found` (404): Not found. A host other than the key's own company is always not found.
- `registered_office` (409): The registered office comes from the company register and cannot be changed with a key.
- `post_changed` (409): The post changed since the If-Match version. Read it again.
- `post_deleted` (409): The post is in the bin.
- `not_draft` (409): Only a draft can be scheduled.
- `slug_taken` (409): Another post already uses that address.
- `rate_limited` (429): Too many requests. Wait for Retry-After.
- `credits_exhausted` (429): This month's API credits are used. They start again on the first of next month; there are no overage charges.
- `credits_unavailable` (503): The API cannot count credits right now. Try again later.

## Webhooks

On Grow, to.co can also send signed events to your own server when something changes, such as a new message or a change to your company details. Set them up in my.to.co under Webhooks.

## Next steps

- [Help: use API keys](https://to.co/en-ca/help/api-keys?format=md)
- [Help: API credits](https://to.co/en-ca/help/api-credits?format=md)
- [Help: set up webhooks](https://to.co/en-ca/help/webhooks-setup?format=md)
- [The to.co MCP server, for AI assistants](https://to.co/en-ca/docs/mcp?format=md)
- [Open my.to.co](https://my.to.co)
