API and MCP server
Everything the interface does can be done from the API, and an AI plugged into the MCP server schedules posts that go through the same approval as yours.
The key
An admin creates it under API. It carries a role (the same roles as the team) and acts on behalf of its creator. Give it the weakest role that is enough.
The key is shown only once, when it is created. After that the database only keeps its fingerprint. Lost, it gets revoked and created again.
A key cannot create another key, manage the team, commit money, export the data or delete an organization. Those need a real browser session.
An example that works
List your connected accounts, to get their ids:
curl https://app.timalio.com/api/v1/accounts \
-H "Authorization: Bearer tml_YOUR_KEY"Create a post:
curl https://app.timalio.com/api/v1/posts \
-H "Authorization: Bearer tml_YOUR_KEY" \
-H "content-type: application/json" \
-d '{"text":"Hello",
"accounts":["<account id>"],
"scheduledAt":"2026-10-01T09:00:00Z"}'The post is created as a draft (its state reads BROUILLON in the response). It will not go out until it has been submitted and approved.
Submit it for approval:
curl https://app.timalio.com/api/v1/posts/<id>/action \
-H "Authorization: Bearer tml_YOUR_KEY" \
-H "content-type: application/json" \
-d '{"action":"SUBMIT"}'What is exposed
| Path | What it does |
|---|---|
| GET /api/v1/accounts | Connected accounts and their ids |
| GET /api/v1/posts | The queue: 200 newest by default. Optional `since`, `until` (ISO 8601), `state` (comma-separated), `limit` (1 to 500) and `before` (the `suivant` cursor of the previous page, null on the last); an unknown parameter is a 400. The MCP `list_posts` tool takes the same |
| POST /api/v1/posts | Create a post, as a draft |
| GET /api/v1/posts/<id> | Read one post |
| POST /api/v1/posts/<id>/action | Submit, approve, request changes, move, delete |
| GET /api/v1/posts/<id>/log | Who did what, and when |
| POST /api/v1/images | Upload an image |
| POST /api/v1/videos | Upload a video (file or public URL) |
| GET /api/v1/slots | The organization’s slots, and its time zone |
| POST /api/v1/slots/next | The next free slot across accounts |
| POST /api/v1/import/preview | Read a file back without writing anything |
| POST /api/v1/import/confirm | Write the import, all or nothing |
| GET /api/rules | Each network’s up-to-date rules |
The actions available on a post are SUBMIT, APPROVE, REQUEST_CHANGES, EDIT, RESCHEDULE, SCHEDULE_DIRECTLY and DELETE. These are the same functions as the interface, not a second set of rules: a limit fixed on one side is fixed on the other.
An unknown field in a write request (for example `format_instagram` instead of the documented name `formatInstagram`) is refused with a 400 that names it and suggests the right name: nothing is ever ignored silently.
Watch two fields that do not behave the same when editing a post: sending `accounts` or `images` replaces what was there, so an empty array clears everything; not sending `variants` keeps them, and only `{}` erases them. Fixing a typo in the common text must not silently erase the adaptation somebody else wrote.
Rate limits
A few routes are capped per organization, on a rolling ten-minute window, to protect Timalio’s own disk and the third parties it calls on your behalf. A refusal answers 429 with a `Retry-After` header, in seconds: the exact time left before the next attempt is allowed, and the error message states the same delay in minutes.
- Uploading an image or a video, directly or through the MCP tools `upload_image`/`upload_video`: 60 per organization every 10 minutes.
- Fetching an image or a video by `url` instead of uploading it counts against its own, stricter cap: 20 per organization every 10 minutes.
- Reading back or writing a CSV import (`/api/v1/import/preview` and `/api/v1/import/confirm` share one counter): 20 per organization every 10 minutes.
- Verifying a Discord webhook, a Mastodon instance or a Telegram chat: 30 per organization every 10 minutes.
English names, French names
Timalio was first written in French, and every name on this page has a French twin. Both are accepted, everywhere, for good: a request may use one or the other, field by field. Sending both names of the same field is fine when the two values are equal, and refused (400) when they differ, naming the two fields. Nothing is picked for you in silence.
| English | French |
|---|---|
| text | texte |
| accounts | comptes |
| scheduledAt | programmeLe |
| firstComment | premierCommentaire |
| variants | variantes |
| comment | commentaire |
| content | contenu |
| altText | texteAlternatif |
| coverOffsetMs | couvertureOffsetMs |
| coverImage | couvertureImage |
| day | jour |
| lines | lignes |
| then | suite |
| SUBMIT | SOUMETTRE |
| APPROVE | VALIDER |
| REQUEST_CHANGES | DEMANDER_REPRISE |
| EDIT | MODIFIER |
| RESCHEDULE | REPROGRAMMER |
| SCHEDULE_DIRECTLY | PROGRAMMER_DIRECTEMENT |
| DELETE | SUPPRIMER |
| DRAFT, SCHEDULE (import) | BROUILLON, PROGRAMMER |
| /api/v1/accounts | /api/v1/comptes |
| /api/v1/slots | /api/v1/creneaux |
| /api/v1/slots/next | /api/v1/creneaux/prochain |
| /api/v1/import/preview | /api/v1/import/apercu |
| /api/v1/import/confirm | /api/v1/import/confirmer |
| /api/v1/import/example | /api/v1/import/exemple |
| …/posts/<id>/log | …/posts/<id>/journal |
| …/posts/<id>/comment | …/posts/<id>/commentaire |
| …/posts/<id>/duplicate | …/posts/<id>/dupliquer |
| /api/rules | /api/regles |
| network_rules | regles_du_reseau |
| list_accounts | lister_comptes |
| list_posts | lister_posts |
| read_post | lire_post |
| schedule_post | programmer_post |
| act_on_post | agir_sur_post |
| upload_image | envoyer_image |
| retry_comment | relancer_commentaire |
| prepare_connection | preparer_connexion |
| cart_status | etat_panier |
| cancel_connection | annuler_connexion |
Responses keep their French names, whatever you send: `texte`, `etat`, `programme_le`, and values such as BROUILLON (draft), EN_ATTENTE (awaiting approval), PROGRAMME (scheduled), PUBLIE (published). One stable shape: a response that changed with the request would break whoever reads back what they wrote. Error messages, on the other hand, come in the language of the key’s owner. Two outputs can be asked for in English with `?names=en`: the MCP tool list, and the header row of the CSV export.
The MCP server
The same key plugs an AI into Timalio. It finds fourteen tools there: read a network’s up-to-date rules, list accounts, list and read posts, schedule one or publish it now, act on a post, upload an image or a video, retry a first comment, and prepare the connection of a new account (below).
{
"mcpServers": {
"timalio": {
"url": "https://app.timalio.com/api/mcp?names=en",
"headers": { "Authorization": "Bearer tml_YOUR_KEY" }
}
}
}HTTP transport. With `?names=en` the tools are listed under their English names, with English arguments; without it, under the French ones. Calls accept both. Each network’s rules are served in the language of the key’s owner.
A post created by an AI does not go out on its own. It is born a draft and follows the same loop as any other: it has to be submitted, and somebody has to approve it. That is the whole point of the loop: an AI that publishes without review is exactly what we do not want to sell.
Adding an account from an AI
`prepare_connection` lets an AI add an account, within two limits it cannot lift: it never gives consent, and it never pays. For the networks that sign in through their own approval screen, it gets a link that works once, for 24 hours. The AI hands it to the owner of the account, who needs no Timalio account: the page shows which organization is asking, then sends them to the network’s own approval screen. For a Discord channel, the AI gives its webhook and the channel is connected after the same checks as the Accounts page. The tool says which networks it handles; the others are connected from the Accounts page.
An account the subscription does not cover yet is not refused: it goes to the cart, inactive, for seven days. `cart_status` gives the AI the accounts waiting, the monthly amount they add and a link to the Accounts page, where an administrator, signed in, reviews the amount and pays. Nothing is charged before that click. An exempt organization has no cart.
Only an ADMIN key creates a link, and only while its owner is still an administrator. At most 10 links per organization over 24 hours. Each link appears on the Accounts page, with what became of it, and can be revoked there or with `cancel_connection`; revoking the key ends its links too. Send a link only to the owner of the account: whoever opens it first can connect their own account with it.
See also
A question this page doesn’t answer? contact@timalio.com