# BaseOS > BaseOS is a multi-tenant business platform (CRM, marketing automation, projects, support, finance). This file indexes its public REST API, outbound webhooks and marketing pages for language models and integration agents. Canonical origin: https://baseos.dev ## When to use BaseOS Reach for this API when the job is one of these: - **Create or update a CRM record from a conversation** — Push a lead, contact, account, or opportunity into a pipeline a human sales team already works in — including the next action and its due date. - `POST /api/v1/leads` - `POST /api/v1/contacts` - `POST /api/v1/opportunities` - `POST /api/v1/activities` - **Read the state of client work** — Answer questions about projects, their tasks and their delivery dates without scraping a UI. - `GET /api/v1/projects` - `GET /api/v1/projects/{projectId}/tasks` - `GET /api/v1/clients` - **Open or advance a support ticket** — File a ticket into the right department, add a reply, and move it along the NEW → OPEN → RESOLVED state machine. - `GET /api/v1/tickets` - `POST /api/v1/tickets` - `POST /api/v1/tickets/{id}/messages` - **Quote, bill, and reconcile** — Build a quote from catalog items, then read the financial accounts and double-entry transactions it turns into. - `GET /api/v1/quotes` - `POST /api/v1/quotes` - `GET /api/v1/items` - `GET /api/v1/financial/transactions` - **Reconstruct what happened to a record** — Read the cross-module activity timeline instead of stitching one together from each resource's list endpoint. - `GET /api/v1/timeline` - `GET /api/v1/activities` - **React to something happening in BaseOS** — Subscribe to outbound webhooks instead of polling. Every delivery is a JSON POST signed with HMAC-SHA256 using the endpoint's secret. - `Settings → Webhooks (/home/settings/webhooks)` - **Work as a tool-using agent inside one organization** — Speak Model Context Protocol against a single tenant, with the same module gating a human member has. Needs an API key carrying the `mcp:use` scope — no existing key inherits it. - `POST /api/v1/mcp` ## When not to use BaseOS - Reading data across organizations. Every API key is scoped to exactly one organization; there is no tenant-wide or cross-tenant read. - Reaching a resource whose module the organization has not activated. That returns 403, and retrying with a different key will not change it. - Anonymous access. There is no public read API — every /api/v1 request needs `Authorization: Bearer `. - Bulk export as a substitute for the product's own export. List endpoints paginate and an out-of-range `limit` is rejected with 400, never clamped. ## How to call it 1. Ask an organization admin for an API key from Settings → API Keys (`/home/settings/api-keys`). 2. Send it on every request: `Authorization: Bearer `. 3. Read `/api/docs/swagger.json` for the exact request and response shape of the endpoint you want. 4. Paginate with `page` and `limit`; read the `pagination` object in the response rather than guessing when to stop. 5. Treat `401` as a bad key, `403` as a module the organization has not bought, and `400` as a request you should fix rather than retry. ```http GET /api/v1/clients?page=1&limit=25 HTTP/1.1 Host: baseos.dev Authorization: Bearer Accept: application/json ``` ## Authentication - Every `/api/v1` request needs an API key: `Authorization: Bearer `. - Keys are minted by an organization admin in Settings → API Keys (`/home/settings/api-keys`). - `401` means the key is missing or invalid; `403` means the key is valid but the organization has not activated the module that owns the resource. - List endpoints paginate with `page` and `limit` query parameters and return a `pagination` object; an out-of-range `limit` is rejected with `400`, never silently clamped. ## REST API - `POST /api/stripe/webhook/{organizationId}`: Stripe webhook endpoint for invoice.paid events - `GET /api/v1/accounts`: List customer accounts - `POST /api/v1/accounts`: Create customer account - `DELETE /api/v1/accounts/{id}`: Delete customer account - `GET /api/v1/accounts/{id}`: Get customer account by ID - `PUT /api/v1/accounts/{id}`: Update customer account - `GET /api/v1/activities`: List activities - `POST /api/v1/activities`: Log an activity - `GET /api/v1/activities/{id}`: Get one activity - `PATCH /api/v1/activities/{id}`: Complete or cancel an activity - `GET /api/v1/clients`: List clients with pagination and search filters - `POST /api/v1/clients`: Create a new client - `DELETE /api/v1/clients/{id}`: Delete client - `GET /api/v1/clients/{id}`: Get client by ID - `PUT /api/v1/clients/{id}`: Update client - `GET /api/v1/contacts`: List CRM contacts (people and companies) - `POST /api/v1/contacts`: Create or update a CRM contact, resolved by identifier - `GET /api/v1/contacts/{id}`: Read one CRM contact - `PATCH /api/v1/contacts/{id}`: Update a CRM contact - `POST /api/v1/events`: Ingest product events - `GET /api/v1/financial/accounts`: List financial accounts - `POST /api/v1/financial/accounts`: Create a financial account - `GET /api/v1/financial/accounts/{id}`: Get financial account by ID - `GET /api/v1/financial/transactions`: List financial transactions - `POST /api/v1/financial/transactions`: Create a financial transaction - `GET /api/v1/financial/transactions/{id}`: Get financial transaction by ID - `GET /api/v1/items`: List items with pagination and filters - `POST /api/v1/items`: Create a new item - `DELETE /api/v1/items/{id}`: Delete item - `GET /api/v1/items/{id}`: Get item by ID - `PUT /api/v1/items/{id}`: Update item - `GET /api/v1/leads`: List growth leads - `POST /api/v1/leads`: Create a growth lead - `GET /api/v1/leads/{id}`: Read one growth lead - `PATCH /api/v1/leads/{id}`: Update a growth lead - `GET /api/v1/opportunities`: List opportunities - `POST /api/v1/opportunities`: Create an opportunity - `GET /api/v1/opportunities/{id}`: Get one opportunity - `PATCH /api/v1/opportunities/{id}`: Update, move or close an opportunity - `GET /api/v1/projects`: List projects with pagination and filters - `POST /api/v1/projects`: Create a new project - `DELETE /api/v1/projects/{id}`: Delete project - `GET /api/v1/projects/{id}`: Get project by ID - `PUT /api/v1/projects/{id}`: Update project - `GET /api/v1/projects/{projectId}/tasks`: List tasks for a project - `POST /api/v1/projects/{projectId}/tasks`: Create a task in a project - `GET /api/v1/quotes`: List quotes with pagination and filters - `POST /api/v1/quotes`: Create a new quote - `DELETE /api/v1/quotes/{id}`: Delete quote - `GET /api/v1/quotes/{id}`: Get quote by ID - `PUT /api/v1/quotes/{id}`: Update quote - `DELETE /api/v1/tasks/{id}`: Delete task - `GET /api/v1/tasks/{id}`: Get task by ID - `PUT /api/v1/tasks/{id}`: Update task - `GET /api/v1/tickets`: List tickets with pagination and filters - `POST /api/v1/tickets`: Create a new ticket - `DELETE /api/v1/tickets/{id}`: Delete ticket - `GET /api/v1/tickets/{id}`: Get ticket by ID - `PUT /api/v1/tickets/{id}`: Update ticket - `POST /api/v1/tickets/{id}/messages`: Add message to ticket - `GET /api/v1/timeline`: Read the interaction timeline of a contact, lead or opportunity - `POST /api/v1/workflows/{id}/enroll`: Enroll leads or contacts into a published workflow ## Webhooks Outbound webhooks are configured per endpoint in Settings → Webhooks (`/home/settings/webhooks`). Each delivery is a JSON POST with the envelope `{ event, organizationId, data, timestamp }`, signed with `X-Baseos-Signature: sha256=` (HMAC-SHA256 with the endpoint's secret). The `X-Baseos-Webhook-Delivery` header is `live` for real events and `test` for deliveries sent from the settings screen, which also carry `test: true` inside the signed body. ### Content - `cms.entry.published`: An entry went live, or a live entry was re-published with new content. This is the one a headless site needs to revalidate. - `cms.entry.unpublished`: A live entry was taken down. Its draft still exists. - `cms.entry.updated`: A draft was saved. Only useful for a site running in preview mode — the public API is unaffected. - `cms.entry.deleted`: An entry was deleted. Fires once per locale that was live at the time. ### CRM - `lead.created`: A lead entered the CRM, from any channel. Carries the entry channel and source so an analytics tool can attribute it. - `lead.updated`: A lead's fields were edited. Carries `changedFields` with the previous and new value of each one, so a receiver can ignore the edits it does not care about instead of re-reading the lead. - `lead.status_changed`: A lead moved between statuses (NEW → QUALIFIED → …). Carries both the previous and the new value. - `lead.owner_changed`: A lead changed hands, including when it was assigned for the first time or left unassigned. This is the one a rep's own notifier listens to. - `lead.score_changed`: A lead's score moved, by a recompute or by hand. Carries the previous total, the new one, the letter grade and the fit/engagement/manual split — never fires when a recompute lands on the same number. - `lead.tag_added`: One or more tags were put on a lead. Carries only the tags that were genuinely new, so re-applying a tag it already had does not fire. - `lead.tag_removed`: One or more tags were taken off a lead. Carries only the tags that were actually there. - `opportunity.created`: A deal entered the pipeline, with its value, currency and opening stage. - `opportunity.stage_changed`: A deal moved to a different stage. Carries the stage it left and the one it entered, both with their names. Re-dropping a deal on the stage it already sits in is not a move and does not fire. - `opportunity.won`: A deal closed won, with its amount and currency. This is the event that lets a campaign be tied to real revenue. - `opportunity.lost`: A deal closed lost, with its reason when one was captured. ### E-mail delivery - `email.delivered`: The provider accepted the message and the receiving server took it. Carries the message and lead ids, never the subject or the body. - `email.opened`: A tracking pixel loaded. Treat it as a hint and not as proof: an image proxy can load it without a human, and a client with images off never will. - `email.clicked`: A link in the message was followed. Carries the url that was clicked, when the provider reported one — the only event of this family that says which link. - `email.bounced`: A hard bounce: that address does not exist. The contact is suppressed on our side at the same time, so a receiver should mirror it rather than retry. - `email.complained`: The recipient marked the message as spam. Suppressed on our side too. This is the one worth waking someone up for — it costs sending reputation. - `email.unsubscribed`: The recipient opted out, through the list-unsubscribe header or the link in the footer. Fires once per address, and again on a repeat click. ### Automations - `workflow.enrolled`: A lead or a contact entered an automation, from any door: a trigger, a bulk enrolment, a campaign or the enrol endpoint. Carries the subject and the run id. - `workflow.completed`: A run reached the end of its definition. Carries the same run id the enrolment did, so a receiver can close its own record of who is in the flow. - `workflow.failed`: A run stopped on an error it will not retry. Carries the classified reason. A transient failure that is going to be retried is not announced here. - `workflow.canceled`: A run was taken out of the flow before finishing — an exit condition, an opt-out, a reply, a campaign that ended or a person pressing cancel. Carries the reason. ### Billing - `invoice.created`: An invoice was issued, with its number, client, total, currency and due date. Fires for every origin — the finance screen, the agent, a subscription renewal, an imported order. - `invoice.paid`: An invoice is settled in full. Fires once, on the transition into PAID — a partial payment is `payment.received`, never this. This is the one that means stop chasing the customer. - `invoice.overdue`: An unpaid invoice passed its due date. Fires once per invoice on the transition, not once per nightly sweep, so a dunning sequence is not restarted every morning. - `invoice.canceled`: An invoice was cancelled. It is no longer owed and no longer chased; carries the status it was cancelled from. - `payment.received`: Money landed against an invoice, in whole or in part. Carries the amount, the method, the running total paid and what is still outstanding — so a receiver can tell a deposit from a settlement without asking. ### Factory - `factory.checkpoint.created`: A stage checkpoint is waiting for a human decision. - `factory.stage.completed`: A factory stage finished. - `factory.budget.low`: A project's remaining budget crossed the warning floor. - `agent.blocked`: An agent stopped and needs input to continue. - `factory.stage.blocked`: The pre-flight refused to start a stage: something it needs is not configured. Distinct from `agent.blocked`, where the work was dispatched and the agent could not do it. - `factory.graduated`: A factory project graduated. ## Reference - [BaseOS API reference](https://baseos.dev/docs/api) — Interactive Swagger UI for every REST endpoint, with request and response schemas and a try-it console. - [BaseOS OpenAPI 3.0 specification](https://baseos.dev/api/docs/swagger.json) — The machine-readable contract the reference is generated from. Import it into Postman, Insomnia, or an SDK generator. - [BaseOS llms.txt](https://baseos.dev/llms.txt) — Every endpoint, webhook event, and authentication rule as one markdown file, written to be read in a single request. - [BaseOS MCP server](https://baseos.dev/api/v1/mcp) — Model Context Protocol endpoint. Point an MCP-capable agent at it with an API key to read and write BaseOS records as tools. - [BaseOS API keys](https://baseos.dev/home/settings/api-keys) — Where an organization admin mints and revokes the bearer tokens every /api/v1 request needs. Requires a signed-in admin. - [BaseOS webhook endpoints](https://baseos.dev/home/settings/webhooks) — Register outbound webhook URLs, read the signing secret, and send a signed test delivery. Requires a signed-in admin. ## Pages - [What BaseOS is, which modules it ships, and what it costs](https://baseos.dev/es) — also available as `text/markdown` - [What BaseOS is, how it is built, how it is sold, and what it commits to](https://baseos.dev/es/about) — also available as `text/markdown` - [REST API, OpenAPI spec, webhooks, and API-key authentication](https://baseos.dev/es/developers) — also available as `text/markdown` - [How to reach sales, support, privacy, and legal](https://baseos.dev/es/contact) — also available as `text/markdown` - [Hosting, encryption, backups, and data-handling commitments](https://baseos.dev/es/security) - [Privacy policy](https://baseos.dev/es/privacy) - [Terms of service](https://baseos.dev/es/terms) Pages are served in English (`/en`), Spanish (`/es`) and Portuguese (`/pt`); swap the prefix for the language you want. Requesting any of them with `Accept: text/markdown` returns the markdown representation instead of the HTML page, and responses carry `Vary: Accept`.