to.co home
Get started

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.

Get an API key Routes

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

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.

ScopeWhat it doesFrom plan
company:readYour company details, services and locations.Pro
messages:readMessages from your contact form (not those held as spam).Pro
company:writeReplace your services, and change phone numbers and opening hours.Grow
posts:readThe posts you write on to.co, drafts included.Pro
posts:writeWrite, 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.

RouteWhat it doesScope
GET /v1/orgs/{host}The company record, with its services and locations.company:read
GET /v1/orgs/{host}/servicesThe services, in order.company:read
GET /v1/orgs/{host}/locationsThe locations, with phones and opening hours.company:read
GET /v1/orgs/{host}/messagesContact 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}/servicesReplace 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}/postsPosts 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}/postsA 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}/publishPublish the If-Match version. It is checked first, as when the owner publishes.posts:write
POST /v1/orgs/{host}/posts/{id}/unpublishTake 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

Language

Choose from the languages the site and the app support in full.