API reference · v1

The operating soul, as an API.

Everything a company plan does, and the engine your own product runs on, over plain HTTP and JSON. The constitution is in the design: no endpoint returns a person’s conversation, a transcript, or a name attached to a mood.

Base URL https://api.coros.ai/v1 · JSON in, JSON out · UTC timestamps

Getting started

Keys are issued per company, in Settings, with the scopes you choose. Every request carries one as a bearer token. Mutations accept an Idempotency-Key, so a retried request never does the thing twice.

curl https://api.coros.ai/v1/picture?period=30d \
  -H "Authorization: Bearer cs_live_…" \
  -H "Accept: application/json"
// JavaScript
import COROS from "@coros/sdk";
const coros = new COROS(process.env.COROS_KEY);

const picture = await coros.picture.get({ period: "30d" });
console.log(picture.moods.leading); // "ambition"
# Python
from coros import COROS
coros = COROS(os.environ["COROS_KEY"])

picture = coros.picture.get(period="30d")
print(picture.promises.kept_on_time)

Scopes

people
Invite, change and remove seats.
priorities
Read and set the agenda.
memory
Read the company memory and its rooms; delete it with the owner key.
meetings
Register meetings and read what was promised in them.
commitments
Read, register and update commitments.
picture
Read the company picture, in aggregate.
agents
Define and run agents.
voice
Read and set the voice and follow-through settings.
webhooks
Manage subscriptions.
engine
Use the engine inside your own product.

What it never returns

Integrity as a service, in the design.

  • 01A person’s conversation, in any form. There is no endpoint for it and there will not be one.
  • 02A name attached to a mood, a breakdown, or a breakthrough. The picture is aggregate, over five or more people, or it is not returned.
  • 03A person’s memory after they leave. It goes with them. What they shared into the company’s memory stays, because it was shared.
  • 04Anything from the engine’s users under a key that did not create them. Your product’s users are yours alone.

A request that would cross one of these lines returns 403 with the code forbidden and the reason. It is the one error that no scope unlocks.

People

The seats. A person is invited, begins with the first conversation, and their COROS is theirs from that moment. When they leave, it leaves with them.

GET /people

List the people on your plan, with seat and status. Never their conversations.

Response

{
  "data": [
    { "id": "per_8f1c", "email": "[email protected]", "seat": "premium", "status": "active", "joined_at": "2026-10-02T09:14:00Z" },
    { "id": "per_2b7e", "email": "[email protected]", "seat": "standard", "status": "invited", "joined_at": null }
  ],
  "has_more": false
}
POST /people

Invite a person to a seat. They receive the first conversation, not a form.

Request

{ "email": "[email protected]", "seat": "premium", "rooms": ["room_lead"] }

Response

{ "id": "per_c044", "email": "[email protected]", "seat": "premium", "status": "invited" }
PATCH /people/{id}

Change a seat, or a person’s rooms.

Request

{ "seat": "standard" }
DELETE /people/{id}

The person leaves the plan. Their COROS and their memory go with them and become their own; what they shared into the company’s memory stays with the company.

Response

{ "id": "per_2b7e", "status": "left", "left_at": "2026-10-15T08:00:00Z" }

Priorities

What the company is up to. Load it once, keep it current, and every person’s COROS carries it into their conversations and shows them where their work meets it.

GET /priorities

The current agenda.

Response

{
  "statement": "Ship the platform to all 40 partner schools by March.",
  "priorities": [
    { "id": "pri_01", "title": "Partner onboarding under two weeks", "by": "2027-03-31", "owner_role": "Head of Partnerships" },
    { "id": "pri_02", "title": "Support answered inside a day", "by": "2027-01-31", "owner_role": "Head of Operations" }
  ],
  "updated_at": "2026-10-10T16:20:00Z"
}
PUT /priorities

Replace the agenda. One statement a CEO could say to the whole company, and the priorities under it.

Request

{ "statement": "…", "priorities": [ { "title": "…", "by": "2027-03-31", "owner_role": "…" } ] }

Company memory

The shared memory: only what passed between people. The concerns, the rooms, the commitments. The company owns it, shapes it, and can delete it.

GET /memory

What the company’s memory holds, in aggregate: rooms, open concerns, the count of commitments, and when it was last written to.

Response

{
  "rooms": 6,
  "concerns": [ { "id": "con_19", "title": "Partner onboarding is slower than promised", "raised_in": "room_lead", "since": "2026-09-28" } ],
  "commitments": { "open": 23, "kept": 118, "renegotiated": 9 },
  "last_written_at": "2026-10-15T07:55:00Z"
}
GET /memory/rooms

The rooms a company’s memory is organised by: a team, a standing meeting, a project.

POST /memory/rooms

Create a room and name who is in it. A team is a shared memory, constituted by what it declared and who is in it.

Request

{ "name": "Leadership weekly", "members": ["per_8f1c", "per_c044"], "declares": "Decisions that hold outside this room." }

Response

{ "id": "room_lead", "name": "Leadership weekly", "members": 2 }
DELETE /memory

Delete the company’s memory. Every person’s own memory is untouched; it was never in here. Irreversible, and yours to do.

Requires the owner key and a second confirmation header, X-Confirm-Delete: company-memory.

Meetings

The meeting companion. Register a meeting and COROS joins it, on Zoom or Google Meet, and keeps who promised what, by when, and what is now at risk. Not minutes.

POST /meetings

Ask the companion to join a meeting.

Request

{ "room": "room_lead", "platform": "zoom", "join_url": "https://zoom.us/j/…", "starts_at": "2026-10-16T15:00:00Z" }

Response

{ "id": "mtg_7a1", "status": "scheduled", "room": "room_lead" }
GET /meetings/{id}

The meeting, after it has happened: who was in it, what was promised, what is at risk.

Response

{
  "id": "mtg_7a1",
  "room": "room_lead",
  "held_at": "2026-10-16T15:00:00Z",
  "commitments_made": 3,
  "at_risk": 1,
  "breakdowns_declared": 1
}
GET /meetings/{id}/commitments

The commitments made in the room, which are shared by nature: they were made out loud, between people.

Commitments

Every request followed to its promise and its completion. A slip is an invitation to talk, never an alarm, so the API tells you when one is slipping and never files a ticket.

GET /commitments

List commitments. Filter by status, room, or due date.

Query: status=open|slipping|kept|renegotiated|dropped · room=room_lead · due_before=2026-10-31

Response

{
  "data": [
    {
      "id": "cmt_51d",
      "request": "The regional forecast, reconciled, with the two board scenarios",
      "requested_by": "per_8f1c",
      "promised_by": "per_c044",
      "due": "2026-10-15T19:00:00Z",
      "status": "slipping",
      "made_in": "mtg_7a1",
      "updated_at": "2026-10-14T22:10:00Z"
    }
  ]
}
POST /commitments

Register a commitment made somewhere COROS was not: in email, in a hallway, in your own system.

Request

{ "request": "…", "requested_by": "per_8f1c", "promised_by": "per_c044", "due": "2026-10-20" }
PATCH /commitments/{id}

Renegotiate, complete, or drop one. Completion needs the requester to say it is done.

Request

{ "status": "renegotiated", "due": "2026-10-22", "by": "per_c044" }

The company picture

The action state, in aggregate: the work moving, the stuckness getting resolved, the moods turning, the breakthroughs arriving. Promises, moods and breakdowns, constantly. Never a transcript, never a name, never a map of who is stuck.

GET /picture

The company as a whole, for the period. Groups smaller than five are never reported.

Query: period=7d|30d|90d

Response

{
  "period": "30d",
  "people_active": 38,
  "promises": { "made": 212, "kept_on_time": 161, "renegotiated_early": 27, "slipped": 24 },
  "breakdowns": { "declared": 41, "resolved": 36, "open": 5 },
  "moods": { "leading": "ambition", "trend": "rising", "mix": { "ambition": 0.41, "resolve": 0.22, "resignation": 0.11, "anxiety": 0.09, "other": 0.17 } },
  "breakthroughs": 23,
  "min_group": 5
}
GET /picture/rooms/{id}

The same picture for one room, when the room has five or more people. Smaller rooms return 204 No Content, on purpose.

The picture is read from conversation, held for the whole company and never for a name. There is no endpoint that returns a person’s moods, breakdowns or words, and there will not be one.

Agents

Agents fitted to your own practices, not to a category. You describe the practice; the agent brings each person what matters, when it matters.

GET /agents

The agents running for your company.

POST /agents

Define an agent from a practice. For a company of coaches: one that follows up with coach and client after every session.

Request

{
  "name": "After every session",
  "practice": "Follow up with the coach and the client so the client gets the most from every session, and learn what made the good sessions good.",
  "trigger": { "event": "session.completed" },
  "audience": ["coaches", "clients"],
  "cadence": "within 2h"
}

Response

{ "id": "agt_3e2", "name": "After every session", "status": "running" }
POST /agents/{id}/run

Run it now, against one event or one room.

Request

{ "room": "room_coaches" }
DELETE /agents/{id}

Stop and remove it.

Voice and settings

How COROS speaks for your company, and how often it comes to find a person. Modes are set at the company level and, above the company’s floor, by each person.

GET /voice

The company’s voice: the default, or the one trained for you.

Response

{ "mode": "trained", "name": "Acme", "tone": "warm, direct", "version": 2, "trained_at": "2026-09-30T00:00:00Z" }
PUT /voice

Switch between the default voice and your trained voice, or set its tone.

Request

{ "mode": "default" }
PUT /settings/follow-through

The company’s floor for how quiet COROS may be. The quietest setting is one note a week, on the company’s behalf. Each person may ask for more, never less than the floor.

Request

{ "quietest": "weekly_note", "default": "when_it_matters" }

Webhooks

Events, as they happen, to your own systems. Signed, retried, and never carrying a transcript.

POST /webhooks

Subscribe a URL to events.

Request

{ "url": "https://acme.com/hooks/coros", "events": ["commitment.slipping", "breakdown.declared", "picture.updated"] }

Response

{ "id": "whk_09a", "secret": "whsec_…" }
GET /webhooks

Your subscriptions.

DELETE /webhooks/{id}

Unsubscribe.

Events: person.joined, person.left, commitment.made, commitment.slipping, commitment.kept, commitment.renegotiated, breakdown.declared, breakdown.resolved, meeting.captured, picture.updated. Every delivery carries a COROS-Signature header, an HMAC of the body with your secret, and is retried with backoff for 24 hours.

The engine, for your own product

The same engine, in the product you build for your customers. Each of your users gets a memory of their own that your product owns, and the engine reads the state of action in their conversations for you.

POST /engine/users

Create a user of your product in the engine. The memory created here belongs to your product, under your key.

Request

{ "external_id": "u_48811", "language": "en", "practice": "worker-abroad" }

Response

{ "id": "eu_91f", "external_id": "u_48811" }
POST /engine/users/{id}/messages

Send a message from your user and receive the reply, streamed. The engine remembers across messages: what they are after, what they promised, how they are.

Request

{ "text": "I promised my manager the report by Friday and I am nowhere near.", "stream": true }

Response

event: delta
data: { "text": "What did you promise, exactly, and to whom?" }

event: state
data: { "commitments": [ { "text": "the report", "to": "my manager", "due": "Friday", "status": "at_risk" } ], "mood": "anxiety", "breakdown": true }

event: done
GET /engine/users/{id}/state

The state of action for one of your users: their open commitments, their mood as last read, their open breakdowns. Yours to show them, in your product, in your words.

DELETE /engine/users/{id}

Delete the user and their memory. Nothing of it is kept.

The engine endpoints are what our partners build on. Everything above them is the company plan; this is the product you sell.

Errors and limits

Errors are JSON, with a code you can branch on and a message a person can read.

{ "error": { "code": "rate_limited", "message": "More than 600 requests in a minute.", "retry_after": 12 } }
400
invalid_request
The body or query did not parse, or a required field is missing.
401
unauthorized
No key, or a key that has been revoked.
403
forbidden
The key lacks the scope, or the endpoint would reveal a person. The second is never granted.
404
not_found
No such resource under this company.
204
no_content
A picture for a group smaller than five. Deliberate.
409
conflict
An Idempotency-Key already used with a different body.
429
rate_limited
More than 600 requests in a minute. Retry-After says when.

600 requests a minute per key. Webhook events: person.joined, person.left, commitment.made, commitment.slipping, commitment.kept, commitment.renegotiated, breakdown.declared, breakdown.resolved, meeting.captured, picture.updated. Versioned in the path; v1 is stable once it is marked so in the changelog, and nothing in it is removed without a year’s notice.

Where this stands

This is the v1 reference, published as we build it with our first partners. The engine endpoints are what they run on today; the company endpoints ship as the plans need them, and the changelog says which are live. If you are building on it, talk to us first and we will build in the order you need.

Book a call with us →