Developer API

Your Portail account, from your own code

Search your memory, read your connected apps, and act through them with one HTTPS call from your server.

Create a key in Portail under Settings > Developer API. Keys come with any paid plan.

Open Portail

What it is, and what it isn't

The API reaches Portail features on your own account. It does not run language models and it does not sell model access, so calls never spend credits. If you want a model's answer, that happens in Portail itself.

Base URL

https://api.portail.cc/api/v1

Authentication

Send the key as a bearer token. Call from your server only: requests carrying a browser Origin header are refused, so a key never ends up in page source.

curl https://api.portail.cc/api/v1/me \
  -H "Authorization: Bearer $PORTAIL_API_KEY"

Permissions

Each key carries only the permissions you picked when you made it. Need a different set? Make another key.

memory:readRead memory. Search what Portail remembers about you.
connectors:readRead connected apps. List events, fetch email, search pages. Nothing changes.
connectors:writeAct in connected apps. Send email, create events, publish posts. Real actions on your accounts.

Endpoints

MethodPathPermissionWhat it does
GET/meAnyWhich key this is and what it can do.
POST/memory/querymemory:readSearch your saved memory. Body: query (1 to 500 characters), limit (1 to 25, default 10), include_active (default true).
GET/connectorsconnectors:readEvery connector, whether it's connected, and each tool with its effect and arguments.
POST/connectors/{connector}/{tool}connectors:read | connectors:writeRun one connector tool. Body: arguments, holding only the names that tool lists. Read tools need connectors:read. Write tools need connectors:write and an Idempotency-Key header.

Examples

curl -X POST https://api.portail.cc/api/v1/memory/query \
  -H "Authorization: Bearer $PORTAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "preferred stack", "limit": 5}'
curl -X POST https://api.portail.cc/api/v1/connectors/google_calendar/list_events \
  -H "Authorization: Bearer $PORTAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"arguments": {"start_period": "2026-10-05T00:00:00Z", "end_period": "2026-10-12T00:00:00Z"}}'
curl -X POST https://api.portail.cc/api/v1/connectors/gmail/send_email \
  -H "Authorization: Bearer $PORTAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: invoice-1042-reminder" \
  -d '{"arguments": {"to": "client@example.com", "subject": "Invoice 1042", "body": "A friendly nudge."}}'

Writes need an Idempotency-Key

Write tools send real email, create real events and publish real posts, so every write carries an Idempotency-Key header you choose (8 to 128 characters, like an order id). Retry with the same key and the same body and you get the first response back, marked Idempotency-Replayed, instead of a second email. Keys are remembered for 24 hours per API key. If a write fails, the key is released and a retry runs it again.

Limits

60 requests per minute per key, up to 5 active keys per account. Each account also gets 2,000 connector reads and 100 connector writes a day, shared across all its keys, reset at midnight UTC. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. Go over any limit and you get 429 with Retry-After.

Errors

Every error is JSON with a stable code, a readable message and the request id. Quote the request id if you write to support.

{
  "error": {
    "code": "insufficient_scope",
    "message": "This key does not have the connectors:write scope.",
    "request_id": "5f0c2a1e-..."
  }
}
StatusCodeMeaning
400idempotency_key_requiredA write was sent without an Idempotency-Key.
401missing_keyNo key in the Authorization header.
401invalid_keyThe key is wrong or was revoked.
403browser_requestThe request came from a browser.
403account_disabledThe account that owns the key was deleted or disabled.
403plan_requiredThe key's owner isn't on a paid plan.
403insufficient_scopeThe key lacks the permission this call needs.
409connector_not_connectedConnect that app in Portail first.
409idempotency_in_progressThe same write is still running. Retry shortly.
422idempotency_conflictThat Idempotency-Key was already used for a different request.
429rate_limit_exceededSlow down. Wait for Retry-After.
429daily_limit_exceededThe account used up today's connector calls. Wait for Retry-After.
502connector_errorThe connected app refused or failed the action.