puertafria

Pro plan

Connect your agent

puertafria has an HTTP API and its own MCP server on top of it. Your agent reads the queue, opens a company and records what happened, over HTTPS and never directly against the database. Same rows, same rules, a second door.

Included in Pro, €9 a month, and in Team. On the Free plan the API answers 403 PLAN_API.

# Claude Code, over MCP
$ claude mcp add puertafria \
    -e PUERTAFRIA_TOKEN=pf_... \
    -- npx -y @puertafria/mcp

# Any client, over HTTP
$ curl -sS https://puertafria.es/api/v1/me \
    -H "Authorization: Bearer $PUERTAFRIA_TOKEN"

Four steps

  1. Mint a key

    On your account page, under the subscription panel. Give it a label that says where it will be used, and tick the box if it may write, which is off by default. The key is shown once and stored as a hash, so nobody can read it back. Connecting an agent is part of Pro. Go to your account.

  2. Send it as a header

    On every request, and nowhere else. Never a query parameter, which a log would keep. Never the session cookie: a signed-in browser must not reach this by accident.

    Authorization: Bearer pf_...
  3. Check what the key can do

    The first call names the account, the plan, and whether this key may write. An agent that reads it first never discovers a limit by being refused.

    curl -sS https://puertafria.es/api/v1/me \
      -H "Authorization: Bearer $PUERTAFRIA_TOKEN"
  4. Work the funnel

    List the funnels, read the queue, open a company, record what happened. Every call goes through the same function the screen goes through, so it meets the same refusals you do.

    curl -sS https://puertafria.es/api/v1/funnels/$FUNNEL/queue \
      -H "Authorization: Bearer $PUERTAFRIA_TOKEN"

The MCP server

It runs over stdio as one process your agent host starts, and offers the same tools as the table below. It holds no logic: each tool is one request to those routes, so the API is the remote part and not the shim. A read-only key is offered only read-only tools, rather than finding a 403 by calling something.

One line in Claude Code, with a key from your account. Cursor, Windsurf and Zed run the same npx -y @puertafria/mcp from their own MCP settings, with the key as PUERTAFRIA_TOKEN.

claude mcp add puertafria -e PUERTAFRIA_TOKEN=pf_... \
  -- npx -y @puertafria/mcp

Every door, and what is behind it

Paths, tool names and refusals stay English in both languages. A translated path is a wrong path.

Reads

GET/api/v1/me
The account this key opens, its plan, and whether this key may change anything.tool: whoami
GET/api/v1/funnels
Every funnel this account holds. Start here: nothing else takes a funnel by name.tool: list_funnels
GET/api/v1/funnels/:id
One funnel: counts by stage, how many are due, its campaigns and provinces, and its tracking fields with their ids, kinds and options, and its saved views.tool: funnel
GET/api/v1/funnels/:id/queue
Who to call today. Takes ?stage= &campaign= &province= &q= &field= &value= (or &from= &to=) &sort= &dir= &page=. A company that asked to stop is never in it.tool: queue
GET/api/v1/companies/:id
One card: the row, its branches, its people, its history, its tracking values, its reminders and its email draft, { subject, body, updatedAt } or null.tool: company
GET/api/v1/funnels/:id/metrics
One funnel's numbers over ?days=7, 30 or 90. Pro and above; the free plan answers 403 PLAN_METRICS.tool: metrics
GET/api/v1/funnels/:id/export/:file
prospects.csv, contacts.csv or activities.csv. For curl and a pipe, never offered as a tool.no tool, on purpose

Writes, and each one needs a key that may write

PATCH/api/v1/funnels/:id
A funnel's new name, as { name }. BAD_NAME when blank or too long, NAME_TAKEN when another funnel has it.tool: rename_funnel
POST/api/v1/companies/:id/activities
What happened on a call. An outcome that leaves the company open needs nextActionAt; a losing one needs a note.tool: record_outcome
PATCH/api/v1/companies/:id/tracking
The funnel's own fields on one company, keyed by field id. A blank clears a value; a field left out is left alone; an unknown id is FIELD_GONE.tool: save_tracking
PATCH/api/v1/companies/:id/branches/:locationId/tracking
The funnel's branch fields on one branch of that company. The same rules; a branch of another company is BRANCH_GONE.tool: save_branch_tracking
PATCH/api/v1/companies/:id/contacts/:contactId/tracking
The funnel's contact fields on one contact of that company. The same rules; a contact of another company is CONTACT_GONE.tool: save_contact_tracking
PUT/api/v1/companies/:id/draft
The company's one email draft, as { subject, body }: at most 200 and 10,000 characters, TOO_LONG past that. An empty body clears it. Never sent by puertafria. SUPPRESSED on a company that asked to stop.tool: save_draft
POST/api/v1/companies/:id/reminders
A promise to come back to this company on a day. Pro and above.tool: add_reminder
PATCH/api/v1/reminders/:id
Tick a reminder off, put it back, or correct its day or note. One of those per call; what is left out stays.tool: update_reminder
PATCH/api/v1/companies/:id/branches/:locationId
One researched fact about one branch of this company. A blank string clears it.tool: edit_branch_fact
POST/api/v1/funnels/:id/activate
Move named companies into a campaign. Ids only, never a filter: read the queue first and send what you looked at. One write per company.tool: activate_cohort
POST/api/v1/funnels/:id/companies
One company typed in, for when there is no file. Bulk loading is the import screen, not this. A match for a company that asked not to be contacted is SUPPRESSED.tool: add_company

What it will refuse

  • Nothing deletes. No route removes a company, a funnel, a field or a reminder. An agent stuck in a loop can make a mess you can read, never one that is gone.

  • A no is forever. A company that asked to stop leaves every queue, and the server refuses another contact against it whoever is asking.

  • An open outcome has to say when it is picked up again, and a losing one has to carry a note. The same rules the screen obeys.

  • 500 write attempts per key per day. Attempts, not successes: a loop being refused is the loop worth stopping. Activating a cohort spends one per company. Reads never spend.

  • A key reads only unless you ticked the box, and you revoke it from the same panel. Revoking takes nothing back that it wrote.

The file to point your agent at

Everything on this page, plus the envelope, the refusal words and the rules behind them, in one plain-text address. That is what an agent should read, and it is the whole contract rather than a summary of one.

Read /llms.txtBack to the home page