---
title: "The API, MCP and connected apps"
slug: features/mcp-and-api
url: https://projectri.com/docs/features/mcp-and-api
section: features
audience: admin
permissions: [integration.api_token, integration.view]
plan: platform.public_api
mobile: false
updated: 2026-09-08
source: Projectri documentation
---

# The API, MCP and connected apps

Three ways software reaches a Projectri workspace — the REST API, the MCP server for coding agents, and third-party apps a member authorises.

![The API, MCP and connected apps](https://projectri.com/docs-shots/admin-integrations-mcp.png)

## What it is

Three surfaces, one access model. The **REST API** at `/api/v1` is for your own
code. The **MCP server** at `/api/mcp` is for coding agents — Claude Code,
Cursor, Codex and anything else that speaks the protocol. **OAuth apps** are for
software somebody else wrote, which a member of your workspace authorises.

All three authenticate with a bearer token, and every one of those tokens has
the same ceiling: **the permissions of the person it belongs to**, recomputed on
every request.

## How to get there

Everything starts at **Admin → Security**:

- **Connect IDE** picks your editor, mints a scoped token and prints the exact
  configuration with the token already in it.
- **API tokens** is the same mint with the full permission picker.
- **Connected apps** lists every third-party application authorised in this
  workspace, who approved it and when it was last used.

Minting or revoking a token needs `integration.api_token`, which ships granted
to ADMIN and can be delegated without promoting anyone. Merely *seeing* which
applications can read your workspace needs only `integration.view` — an auditor
asking "what has access to our data" should not need the key that grants it.

The REST API needs the `platform.public_api` entitlement, which starts at the
Team plan.

## How to use it

### Mint a token

1. **Admin → Security → API tokens**, or **Connect IDE** for one of three
   presets: read-only, read and write, or everything including financials.
2. The plaintext starts `pri_` and is **shown once**. Only its hash is stored.
3. A preset arrives already narrowed to your own access, with anything dropped
   named on the button. **You cannot grant a scope you do not hold yourself.**
4. Revoking is a timestamp, not a delete — an audit trail that loses the row it
   refers to cannot say what the token did before it was killed.

### Call the REST API

1. `Authorization: Bearer pri_…`. Header only; a query parameter would leak the
   secret into access logs and `Referer` headers.
2. `GET /api/v1` lists the resources: **projects, tasks, time-entries, expenses,
   clients, milestones, comments, users**. Each names the scope it reads and
   writes with.
3. `GET /api/v1/openapi.json` is the full specification, public and
   unauthenticated — evaluating whether to integrate should not need a
   credential.

The conventions are identical on every endpoint, by construction:

- **Pagination is cursor-based**, never offset. `limit` defaults to 50 and caps
  at 200; an over-large limit is clamped, not rejected. There is no total —
  an exact count on a filtered table is a second full scan on every page.
- **`?fields=id,name,status`** prunes the response. `id` is always kept.
- **Money is always `{ amount, currency }`** with the amount in integer minor
  units. Never a float, never a bare number.
- **Dates are ISO 8601.** An unparseable one is an error rather than a silently
  ignored filter.
- **Errors are RFC 7807 problem documents** with `application/problem+json` and
  a stable `code`, so you can tell one of our errors from a proxy's HTML 502.
- **Every response carries `X-Request-Id`.** Send your own and it is echoed
  back, so one id traces across both systems.

### Connect a coding agent over MCP

1. **Admin → Security → Connect IDE**, pick the editor, copy what it prints.
2. The endpoint is `POST https://<your-host>/api/mcp`, streamable HTTP and
   stateless.
3. Prefer the environment-variable form. A `.mcp.json` at a repo root gets
   committed, and a token in it is a credential that acts as a real member of
   your workspace, in version control.
4. The tool list is filtered per request to what the token may actually call, so
   an agent is never shown a tool it can only fail at.

The server exposes 49 tools — 39 reads and 10 writes — curated around whole
questions rather than mirrored from the roughly 120 HTTP routes, because every
tool definition costs the model context on every turn.

Reads cover projects, tasks, sprints, notes, documents, the knowledge base,
**this documentation**, clients, estimates, contracts and contract burn, the
sales pipeline and its forecast, people, skills, availability, staffing
scenarios, demand forecasting, bookings, dashboards and reports, and four
finance reads — utilisation, project profitability, work in progress and finance
exceptions.

Writes: create a task, update a task, comment on a task, move a task to a
sprint, log a time entry, create a note, create a project, create a milestone,
create a booking, create an estimate. **Nothing deletes.** To retire a task, set
its status to Cancelled.

Four resource types (`projectri://project/{id}` and the same for tasks, notes
and documents) and three prompts — `standup_summary`, `sprint_review`,
`project_health` — are available for hosts that support them.

### Authorise a third-party app

1. The application sends you to a consent screen naming itself and exactly what
   it is asking for.
2. **The scopes recorded are the request intersected with your own live
   permissions**, and the screen shows which keys that drops. An app asking for
   financial reporting because some of its users are finance admins is still
   installable by everybody else — it just gets less.
3. A narrowing to *nothing* is refused. A token that authenticates and can do
   nothing is an install that looks connected and fails every call.
4. Review and revoke at **Admin → Security → Connected apps**. The list is
   workspace-wide, not per person, with the approver's name against each row and
   a live count of outstanding credentials — how many are actually out there is
   what tells you whether a disconnect is urgent.

## What it affects

- **A token is not an identity.** It acts as the person who created it, and its
  access is recomputed on every request as *the owner's live permissions
  intersected with the token's stored scopes*. Demote the owner, suspend them or
  remove them from the workspace and the token narrows or dies on its next call.
  Stored scopes are a ceiling, never a grant.
- **A token with no scopes authenticates and can do nothing.** That is not a
  bug; it is the empty intersection.
- **Writes fire everything the screen fires** — the notification, the timeline
  entry, the watcher subscription, the automation rule. A task created by an
  agent is indistinguishable in effect from one created by hand, which is the
  point, and the reason the timeline marks it.
- **The activity feed keeps the person in bold** and adds a byline reading *via
  API token "…"*. Work should not appear in a timeline with nothing behind it.
- **Every API call is metered** against `platform.api_calls` and appears in the
  workspace's request log, including the ones that fail — a 404 costs what a 200
  costs, and an integrator asking why their calls stopped is answered from the
  same log as every other question about them.
- **Rate limit: 300 requests per minute per token**, published in
  `X-RateLimit-Limit` on every response. MCP and REST share one bucket, because
  they are the same credential hitting the same database.

### Reading a refusal

| Status | `code` | Means |
|---|---|---|
| 401 | `missing_token`, `invalid_token`, `expired_token`, `revoked_token` | the credential |
| 403 | `no_membership`, `insufficient_scope` | mint a broader token, or ask an admin |
| 403 | `revoked_grant` | the workspace disconnected the app; send the user back through consent |
| 402 | `plan_required` | nothing a token can fix — the workspace has not bought it |
| 429 | `quota_exceeded` | the month's API calls are spent |
| 429 | `rate_limited` | slow down |

`insufficient_scope` and `plan_required` are deliberately different codes.
Both would otherwise arrive as "access denied" and send an integrator on a long
hunt through scope settings for a commercial problem.

## On mobile

There is no token management in the phone app. Everything here is one-time
configuration done at a desk, usually with a vendor console or an editor open
beside it.

## Limits and gotchas

- **Assigning work to somebody else needs `task.assign` on top.** Assigning to
  yourself does not — picking up your own work through an agent should not need
  a permission that picking it up in the interface does not.
- **A read-scoped token cannot write anything.** This is marginally stricter
  than the app in one place: the comment route has no role guard, so a VIEWER
  can comment in the interface but a VIEWER's token cannot.
- **Use `idempotency_key` on anything that creates a row.** A retry with the
  same key returns the original result and does nothing else; reusing a key with
  *different* arguments is an error, not a replay. Keys are scoped per token and
  live 24 hours.
- **Ids are UUIDs**, not the `APO-42` identifier — that is a display name.
- **Private notes stay private.** Workspace scope alone is not enough for
  documents: a private note is readable only by its author, explicit share
  targets, and admins, exactly as in the interface.
- **The workspace IP allowlist is not enforced on the API.** It is not enforced
  on the browser path either; wiring it for tokens alone would be a silent
  inconsistency.
- **The rate limit is honest but shared.** It is enforced by a shared counter,
  and if that counter is unreachable the request is allowed rather than refused
  — a limiter that starts refusing everybody when the database hiccups is an
  outage with a security label on it.
- **`.well-known` discovery is served for OAuth clients** at
  `/.well-known/oauth-authorization-server` and
  `/.well-known/oauth-protected-resource`. PKCE with S256 is mandatory; `plain`
  is not offered.

## Related

- [Security Center](/docs/admin/security) — where tokens are minted and apps are reviewed.
- [MCP servers](/docs/admin/integrations-mcp) — the other direction, Projectri calling out.
- [How permissions work](/docs/features/permissions-model) — the ceiling every token is under.
- [Plans and entitlements](/docs/features/plans-and-entitlements) — `platform.public_api` and the call quota.
- [Integrations](/docs/features/integrations) — connectors we wrote, rather than software you drive.

## Related

- [Security](https://projectri.com/docs/admin/security.md): The sign-in rules this workspace enforces, who is signed in, what machine credentials and outside applications can reach it, and the sign-in history.
- [MCP servers](https://projectri.com/docs/admin/integrations-mcp.md): Register outside MCP servers so this workspace's automations can call their tools, choose which tools are callable, and read what was sent.
- [How permissions work](https://projectri.com/docs/features/permissions-model.md): The three tiers Projectri decides "may this person do this" with, how a role default becomes one person's answer, and the two traps that catch every new key.
- [Plans and entitlements](https://projectri.com/docs/features/plans-and-entitlements.md): What each plan includes, what happens when you hit a limit, and why losing a feature never loses the data behind it.
- [Integrations](https://projectri.com/docs/features/integrations.md): The tools Projectri can connect to, what each connection actually does, how credentials are stored, and what a readiness badge means.
