---
title: Plainrouter coding-agent guide
description: How coding agents should choose a Plainrouter interface, authenticate safely, preserve idempotency, handle errors, and respect approval boundaries.
last_updated: 2026-10-29
---

# Plainrouter coding-agent guide

> Use this guide when writing code that connects a service, automation, or MCP client to Plainrouter. The complete product is available on every published pricing band, registration and production-token management are self-serve, and consequential advertising work remains behind the human and workspace controls described below.

For a repository-level integration, follow the standalone [coding-agent quickstart](https://plainrouter.com/agent-quickstart.md). It tells an agent how to inspect the codebase, choose a client, configure production access safely, and keep credentials out of prompts and source control.

## What Plainrouter provides

Plainrouter is a Meta ads platform for conversion tracking and ad changes with approval controls, not an AI model or LLM router.

Plainrouter has three product surfaces:

- **Signals** accepts consent-aware first-party conversion events and exposes delivery evidence.
- **Actions** turns evidence into policy-gated advertising proposals.
- **Launch** prepares creative and campaign workflows under Ask, Custom or Full.

Do not treat Plainrouter as a general-purpose advertising-platform credential proxy. It does not expose underlying Meta, Google, or other provider credentials.


## Pricing and access facts

- Counting is free on every plan. Your price follows your ad spend band.
- Published flat monthly bands are **0 €, 19 €, 39 €, 79 €, 149 €, 279 €, 479 €, 799 €**. Your pricing band is set by the previous calendar month's observed platform ad spend and billed in arrears. Teams over 100 000 € contact Plainrouter for custom terms.
- Free keeps 30 days of history. Every paid band keeps 12 months.
- Your pricing table is pinned when your first paying invoice is reserved and stays pinned.
- Registration is self-serve and requires no contact-sales form. After workspace and advertising-account setup, the workspace owner can generate, scope, rotate, and revoke production API tokens in the product.

Use the canonical [machine-readable pricing contract](https://plainrouter.com/pricing.md) for eligibility, billing examples, and the complete feature matrix.

## Choose the right interface

| Task                                                                | Interface         | Start here                                   |
| ------------------------------------------------------------------- | ----------------- | -------------------------------------------- |
| Send server-side conversions or read delivery evidence              | Conversion API    | `https://plainrouter.com/api/v1`             |
| Inspect an approved account or propose governed work interactively  | MCP               | `https://plainrouter.com/mcp`                |
| Inspect the complete REST contract                                  | OpenAPI 3.1       | `https://plainrouter.com/openapi.json`       |
| Use a packaged TypeScript, Python, Ruby, Go, or command-line client | Official clients  | `https://plainrouter.com/docs`               |

Use the [documentation sitemap](https://plainrouter.com/sitemap.md) for the complete public hierarchy, the [Plainrouter glossary](https://plainrouter.com/glossary) to resolve product and governance terminology, and [llms-full.txt](https://plainrouter.com/llms-full.txt) when one-response context is preferable.

## Install an official client

- TypeScript SDK: `npm install @plainrouter/sdk@0.6.0` from [`@plainrouter/sdk`](https://www.npmjs.com/package/@plainrouter/sdk).
- Python SDK: `python -m pip install plainrouter==0.6.0` from [`plainrouter`](https://pypi.org/project/plainrouter/).
- Ruby SDK: `gem install plainrouter-sdk -v 0.2.0` from [`plainrouter-sdk`](https://rubygems.org/gems/plainrouter-sdk).
- Go SDK: `go get github.com/plainrouter/sdk-go@v0.6.0` from [`plainrouter/sdk-go`](https://github.com/plainrouter/sdk-go).
- CLI with npm: `npm install --global @plainrouter/cli@0.6.0` from [`@plainrouter/cli`](https://www.npmjs.com/package/@plainrouter/cli).
- CLI with PyPI: `pipx install plainrouter==0.6.0` from [`plainrouter`](https://pypi.org/project/plainrouter/).
- CLI with Homebrew: `brew install plainrouter/tap/plainrouter` from the [`plainrouter/homebrew-tap`](https://github.com/plainrouter/homebrew-tap).

TypeScript SDK `0.5.0`, Python SDK and CLI `0.5.1`, Go SDK `0.5.0`, npm CLI `0.5.0`, and Ruby SDK `0.1.0` use the signed Plainrouter API contract `0.5.0`. Read [`plainrouter/sdk/AGENTS.md`](https://github.com/plainrouter/sdk/blob/main/AGENTS.md) before changing an SDK integration.

## Preview a change

Sign up free at [Plainrouter](https://plainrouter.com/register), authorize access and connect an ad account. Then call `dry_run_actions` to preview a proposed change against account policy and admission without writing.

## Integrate the production Conversion API

Read these contracts before generating client code:

- [Conversion API documentation](https://plainrouter.com/api/docs)
- [OpenAPI 3.1 specification](https://plainrouter.com/openapi.json)
- [Versioning and deprecation policy](https://plainrouter.com/api/versioning)
- [Webhook documentation](https://plainrouter.com/webhooks.md)

Production requests use a customer bearer secret:

```http
Authorization: Bearer YOUR_CUSTOMER_API_SECRET
Accept: application/json
Content-Type: application/json
```

Keep the secret in server-side credential storage. Never place it in browser code, source control, URLs, prompts, logs, fixtures, or generated examples.

When submitting a conversion event:

1. Send the required `event_name` and `consent_basis` fields.
2. Supply a stable `event_id` or `Idempotency-Key` header. If both are present, they must match.
3. Represent money values as decimal strings.
4. Preserve consent evidence exactly as defined by the current OpenAPI schema.
5. Treat HTTP `202` as a newly accepted event and HTTP `200` with `duplicate: true` as an idempotent retry.
6. Follow the returned `Location` header to inspect delivery state.

Do not invent endpoint fields from prose. Generate or validate request types against the current OpenAPI document.

## Connect through MCP

Use the Streamable HTTP endpoint:

```text
https://plainrouter.com/mcp
```

The production endpoint allows the protocol handshake, discovery catalogs, and static `ui://` app shells without a credential. Tool calls and other protected methods require a workspace credential: a workspace key (the documented path) or a workspace-scoped OAuth execution token. OAuth management credentials, management keys and Signals secrets are refused. They return `401` with a `WWW-Authenticate` resource metadata hint without a valid credential.

The [MCP quickstart](https://plainrouter.com/quickstart-mcp.md) covers account setup, authorization and previews.

Discover the connection through the [MCP server card](https://plainrouter.com/.well-known/mcp/server-card.json), [MCP manifest](https://plainrouter.com/.well-known/mcp), and [authentication guide](https://plainrouter.com/auth.md).

First, the human creates a workspace key in **Settings → Workspace keys** and stores it in secure client credential storage. Then configure the MCP client with `https://plainrouter.com/mcp` and the key as its bearer credential. OAuth management credentials cannot authenticate to the MCP server.

A workspace key authorizes one or more workspaces and acts on one at a time; account-scoped tools select an eligible account inside it, while older account-bound keys retain their original restriction. Do not select an unauthorized account, broaden scope after a `403`, or ask the user for a password, social-login credential, authorization code, provider credential, or bearer token.

The `actions.propose` permission authorizes governed proposals, not direct campaign mutation. Ask waits for approval; in Custom, the workspace's Custom switches decide; Full runs policy-allowed agent changes automatically. Policy, dispatch checks, and verification remain authoritative.

## Retry and error behavior

- `400` or `422`: fix the request; do not retry unchanged input.
- `401`: discard or stop using the rejected credential and follow the advertised authentication metadata.
- `403`: stop and request human review; do not attempt to broaden account access or permissions.
- `404`: verify the authenticated workspace owns the requested resource.
- `429`: wait at least the number of seconds in `Retry-After`; add jitter when workers share a credential.
- `5xx`: retry only safe reads or idempotent writes with bounded exponential backoff.

Read `RateLimit` and `RateLimit-Policy` from each response instead of assuming a fixed limit: `r` is the quota you have left, `q` and `w` are the quota and window in force. On a `429`, honour `Retry-After` over the window in `t`. Never retry approval, token, or non-idempotent write operations indefinitely.

## Coding requirements

- Keep production secrets and OAuth tokens out of client-side bundles and generated logs.
- Use exact API versions and published schemas; do not scrape the human dashboard.
- Preserve idempotency keys across retries.
- Treat cursor values as opaque and follow returned pagination URLs.
- Surface approval-required, revoked, forbidden, and rate-limited states to the human operator.
- Record response identifiers needed to retrieve delivery evidence, but do not log personal data or credentials.

## Operational boundaries

Ask waits for approval; in Custom, the workspace's Custom switches decide; Full runs policy-allowed agent changes automatically. Rule violations are blocked and shown in the Inbox. New ads are created paused, provider writes require verification evidence, and authorization stays within the workspace and any account restriction of the credential. A coding agent must not claim that a proposal was approved, executed, or delivered unless the corresponding Plainrouter response provides that evidence.

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
