For developers
The Meetowt API
Read your company's staff list, contacts, tasks and meetings from your own tools, and add or change contacts and tasks. An administrator makes a key; the tool sends it; that is the whole of the set-up.
The API is an extra. It is switched on for a company by Meetowt, on request — write to help@meetowt.com from your administrator's address. Until it is on, your administrators see no key screen here and no key answers.
1. The short version
Your workspace is at its own address, for example acme.meetowt.com. Every route below is under https://<your workspace>/api/v1, answers JSON, and is opened by a key in the Authorization header.
curl https://acme.meetowt.com/api/v1/me \
-H "Authorization: Bearer mwk_YOUR_KEY_HERE"
{ "data": {
"key": { "name": "Accounting sync", "prefix": "mwk_a1B2",
"scopes": ["people:read", "contacts:read"],
"createdAt": "2026-10-11T09:14:00.000Z", "expiresAt": null },
"company": { "name": "Acme Ltd" } } }
2. Making a key
An administrator opens Admin → Company settings → API keys, presses Make a key, gives it a name, ticks what it may do and chooses how long it lasts (30 days, 90 days, a year, or until revoked). The key is shown once, with a Copy button. Meetowt keeps no copy it could show you again — only a one-way fingerprint — so a lost key is revoked and a new one made.
- A key looks like
mwk_followed by 40 letters and digits. - A company can have 25 live keys at once. Make one per tool, so one can be revoked without stopping the rest.
- The name appears in your company's record of changes beside everything the key writes, as
api:<name>. Every other administrator is told, in Meetowt, when a key is made or revoked. - A key acts for the whole company, not for any one person. It is not tied to a colleague's account, so it keeps working when people join and leave — and whoever holds it can do what its scopes say, from anywhere. Keep it out of chat, email and code other people can read.
- Revoking is immediate: the very next request with the key is refused.
3. Authentication
Send Authorization: Bearer mwk_… on every request. Nothing else opens the API: a browser that is signed in to Meetowt gets the same answer as a stranger, and a key does not open anything outside /api/v1.
The company is the one whose address you asked. A key made by another company is not a key here.
Scopes
Each key is allowed some of these, and nothing else exists — no mail, chat, files, employment records or admin.
| Scope | Lets the key |
|---|---|
people:read | Read the staff list. |
contacts:read | Read the company's address book of people outside it. |
contacts:write | Add, change and remove contacts. |
tasks:read | Read every task in the company. |
tasks:write | Give a named person a task, and change a task's status, priority, due date and title. |
calendar:read | Read the company's meetings. |
4. Conventions
- JSON in, JSON out. Send
Content-Type: application/jsonwith a body. A single thing comes back as{ "data": { … } }, a list as{ "data": [ … ], "nextCursor": "…" }. An error is{ "error": "a sentence" }. - Nothing is cached. Every answer says
Cache-Control: no-store. - Times are ISO 8601. A calendar day is
YYYY-MM-DD. People are named by their username. - Paging. A list takes
?limit=(1 to 100, default 50) and?cursor=. WhennextCursoris notnull, pass it back ascursorfor the next page. A cursor is opaque: take it as it comes, and use it with the same list and the same filters. Alimitoutside 1–100, or a cursor that is not one the list gave, is a 400 — never quietly changed. - A body is read strictly. A field the API does not know is refused by name (
unknown field "dueon") rather than ignored, so a misspelling cannot silently lose a value.
5. Routes
GET /api/v1/me
Who this key is. Any valid key may ask; it needs no scope.
GET /api/v1/people — people:read
The staff list, by username: what the company's own directory shows a colleague. No personal address, staff number, phone number or role. People who have left are not listed.
curl "https://acme.meetowt.com/api/v1/people?limit=2" \
-H "Authorization: Bearer mwk_YOUR_KEY_HERE"
{ "data": [
{ "username": "ann", "name": "Ann Hart", "email": "ann.hart@acme.com",
"title": "Buyer", "departments": ["Sales"], "manager": "mona" },
{ "username": "bob", "name": "Bob Samir", "email": "bob@acme.meetowt.com",
"title": null, "departments": [], "manager": null } ],
"nextCursor": "WyJwZW9wbGUiLCJib2IiXQ.4f9c1d2a8b3e" }
email is the address the person is known by at the company. manager is a username, or null at the top.
GET /api/v1/contacts — contacts:read
The company's address book of people outside it — customers, suppliers, partners — by id. Optional ?q= (searches name, address and organisation) and ?tag=.
curl "https://acme.meetowt.com/api/v1/contacts?tag=supplier&limit=1" \
-H "Authorization: Bearer mwk_YOUR_KEY_HERE"
{ "data": [
{ "id": 12, "name": "Mona Hassan", "email": "mona@nile.example",
"phone": "+20 100 000 0000", "organisation": "Nile Freight", "title": "Buyer",
"notes": null, "tags": ["supplier"],
"createdBy": "api:Accounting sync", "createdAt": "2026-10-11T09:20:00.000Z",
"updatedBy": "api:Accounting sync", "updatedAt": "2026-10-11T09:20:00.000Z" } ],
"nextCursor": null }
GET /api/v1/contacts/{id} — contacts:read
One contact, in the same shape, as { "data": { … } }. An id nobody made is a 404.
POST /api/v1/contacts — contacts:write
Adds a contact. Only name is required; the rest are optional: email, phone, organisation, title, notes and tags (single words). The rules are the Contacts screen's: one contact per email address, up to 20,000 a company. Answers 201 with the contact.
curl -X POST https://acme.meetowt.com/api/v1/contacts \
-H "Authorization: Bearer mwk_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"name":"Mona Hassan","email":"mona@nile.example","tags":["supplier"]}'
{ "data": { "id": 12, "name": "Mona Hassan", "email": "mona@nile.example", … } }
A second contact with an address the company already has is a 409 that names the first: Mona Hassan already has that email address — open that contact instead of adding another.
PATCH /api/v1/contacts/{id} — contacts:write
Changes only the fields you send and keeps the rest. Send at least one. An empty string clears a field.
curl -X PATCH https://acme.meetowt.com/api/v1/contacts/12 \
-H "Authorization: Bearer mwk_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"title":"Head of buying"}'
DELETE /api/v1/contacts/{id} — contacts:write
Removes the contact for good. Answers { "data": { "id": 12, "deleted": true } }.
GET /api/v1/tasks — tasks:read
Every task of every person the company has — a key acts for the company, so it is not limited to one person's reporting line. By id. Optional ?status= (todo, doing or done) and ?assignee= (a username).
curl "https://acme.meetowt.com/api/v1/tasks?status=doing" \
-H "Authorization: Bearer mwk_YOUR_KEY_HERE"
{ "data": [
{ "id": 31, "title": "Send the invoice", "detail": "Before Friday.",
"status": "doing", "priority": "high", "assignee": "ann",
"assignedBy": "api:Accounting sync", "dueOn": "2026-10-16",
"tags": [], "steps": [{ "title": "Check the total", "done": false }],
"createdAt": "2026-10-11T09:30:00.000Z",
"startedAt": "2026-10-11T10:05:00.000Z", "finishedAt": null } ],
"nextCursor": null }
POST /api/v1/tasks — tasks:write
Gives a named person a task. assignee (a username the company has) and title (2 to 200 characters) are required; detail, dueOn (YYYY-MM-DD) and priority (low, normal, high or urgent) are optional. The task shows as given by api:<your key's name>, and the person is told as they would be of any task. Answers 201.
curl -X POST https://acme.meetowt.com/api/v1/tasks \
-H "Authorization: Bearer mwk_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"assignee":"ann","title":"Send the invoice","dueOn":"2026-10-16","priority":"high"}'
A username the company does not have is a 404: there is nobody at this company with that username.
PATCH /api/v1/tasks/{id} — tasks:write
Changes a task's status, priority, dueOn (or null to clear it) or title — and nothing else; there is no way to reassign or delete a task through the API. Send at least one field. Finishing a repeating task still makes its next one.
curl -X PATCH https://acme.meetowt.com/api/v1/tasks/31 \
-H "Authorization: Bearer mwk_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{"status":"done"}'
GET /api/v1/calendar/events — calendar:read
The company's meetings between two days. from and to are both required and at most 31 days apart.
Only meetings. A calendar entry that only one person's diary holds — an appointment, a day blocked out — is private to them and is never sent. Neither is a cancelled meeting, nor any meeting's description.
curl "https://acme.meetowt.com/api/v1/calendar/events?from=2026-10-12&to=2026-10-18" \
-H "Authorization: Bearer mwk_YOUR_KEY_HERE"
{ "data": [
{ "id": "ann!4f2c@meetowt", "title": "Budget review",
"start": "2026-10-12T09:00", "end": "2026-10-12T10:00",
"timeZone": "Africa/Cairo",
"startUtc": "2026-10-12T07:00:00.000Z", "endUtc": "2026-10-12T08:00:00.000Z",
"allDay": false, "organizer": "ann", "attendees": ["ann", "bob"] } ],
"nextCursor": null }
start and end are the wall-clock time in timeZone — what the people in the meeting see. startUtc and endUtc are the same moments in UTC (or null if the zone is not one we can convert).
6. Errors
| Status | What it means |
|---|---|
| 400 | The request was not understood: a field that does not exist, a value that is not allowed, a limit outside 1–100, a cursor that is not one the list gave. The sentence says which. |
| 401 | {"error":"invalid or missing API key"} — always these words, whatever was wrong: no key, a malformed one, one nobody made, a revoked one, an expired one, or one from another company. We do not say which, so that nobody can use the answer to find out. |
| 403 | The key is real but is not allowed to do this. The sentence names the scope it needs. |
| 404 | The thing is not there, or the company does not have the API switched on. |
| 409 | It conflicts with what the company has: a contact whose email address is already taken, or the address book is full. |
| 423 | The company's workspace is on hold. The sentence says why. |
| 429 | Too many requests — see below. Retry-After: 60. |
| 503 | We could not check the request just now. Try again in a moment; the key is fine. |
7. Limits
- 600 requests a minute for each key. The rest of that minute are 429.
- 30 wrong or missing keys a minute for each source. A source (an IPv6 address counts as its /64) that presents more is refused outright for the rest of the minute — the right key included, so that a guess cannot be checked. If your tool starts getting 429 after a typo in a key, fix the key and wait a minute.
- Bodies are small: contacts and tasks take up to a few tens of kilobytes. Lists are never longer than 100.
8. Looking after a key
- Give every tool its own key and its own name, and tick only what it needs.
- Choose an expiry for anything that is not permanent.
- Keep it in the tool's secret store or environment, not in code or chat.
- If it may have leaked, revoke it in Admin → Company settings → API keys and make another. Look at the record of changes for what
api:<name>did.