# Connect any agent

Source: https://pushary.com/docs/agents/connect-any-agent
Last updated: 2026-10-18

> Connect any agent to Pushary over MCP or plain HTTP so it can ask you a question on your phone and wait for the answer. Works with n8n, Zapier and LangChain.

Pushary works with any agent that can call an MCP server or send an HTTP request: an ops bot, a research agent, an n8n or Zapier workflow, a LangGraph graph, or a Python script. The agent sends you a question, your phone shows it, and the agent continues with your answer. You need an API key and nothing else.

Set `PUSHARY_API_KEY` to your key, then ask your first question:

```bash
curl -X POST https://pushary.com/api/v1/server/ask \
  -H "Authorization: Bearer $PUSHARY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question":"Deploy the new build to production?"}'
```

Your phone shows a yes or no question. Answer it and the call returns `"answered": true` with `"value": "yes"` or `"value": "no"`.

<Callout type="info">
  Using Claude Code, Codex, Gemini CLI, Cursor or Hermes? Run the [quickstart](/docs/agents/quickstart) instead. One command installs the hooks, so risky actions stop until you approve them.
</Callout>

## Get your API key [#get-your-api-key]

<Steps>
  <Step>
    Open **Settings > API keys** at [pushary.com/dashboard/agent/settings](https://pushary.com/dashboard/agent/settings) and click **Create Key**. The key looks like `pk_xxx.xxx`.
  </Step>

  <Step>
    Export it where your agent runs:

    ```bash
    export PUSHARY_API_KEY="pk_xxx.xxx"
    ```

    Already ran `npx pushary@latest setup`? The key it saved works for `/ask` and `/send` too. The subscriber list, campaign, template and flow endpoints need a key created here.
  </Step>

  <Step>
    Run the curl command above. Your phone shows the question.
  </Step>
</Steps>

No question on your phone? Install the Pushary app from the [App Store](https://apps.apple.com/us/app/pushary/id6785677563) or [Google Play](https://play.google.com/store/apps/details?id=com.pushary.app), sign in with the same account, and try again.

<Callout type="warn">
  Pushary can only block an action before it runs when the agent has a hook for it, as in the [supported agents](/docs/agents/supported-agents) with enforced approvals. Any other agent gets questions and notifications. It decides for itself when to ask and must respect your answer.
</Callout>

## Ask a question and wait for the answer [#ask-a-question-and-wait-for-the-answer]

`POST https://pushary.com/api/v1/server/ask` sends the question to your phone app. It can also reach your browser and Slack, if you connected them. By default the call blocks until you answer, for up to 30 seconds.

```bash
curl -X POST https://pushary.com/api/v1/server/ask \
  -H "Authorization: Bearer $PUSHARY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "question": "Send the campaign to all 4,000 contacts?",
    "type": "confirm",
    "agentName": "Marketing agent",
    "timeoutSeconds": 50
  }'
```

An answered question returns:

```json
{
  "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "question": "Send the campaign to all 4,000 contacts?",
  "type": "confirm",
  "status": "answered",
  "answered": true,
  "value": "yes"
}
```

The response also carries `liveUrl` (a page where you can answer), `pollUrl`, `expiresInSeconds` (600) and `mode` (your approval mode).

Proceed only when `answered` is `true` and `value` is `"yes"`. Anything else is not a yes.

| Result                                               | What it means                                                        | What to do                              |
| ---------------------------------------------------- | -------------------------------------------------------------------- | --------------------------------------- |
| `"status": "answered"`                               | You answered. `value` holds the answer.                              | Act on `value`.                         |
| `"status": "timeout"`                                | The call stopped waiting. The question is still open for 10 minutes. | Poll `pollUrl`, or stop and do not act. |
| `"status": "stopped"` with `"handoffAction": "stop"` | The kill switch is on for this agent. Nothing was sent.              | Stop the run.                           |

### Request fields [#request-fields]

| Field            | Required     | Notes                                                                                                                   |
| ---------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------- |
| `question`       | Yes          | Up to 8000 characters.                                                                                                  |
| `type`           | No           | `confirm` (default), `select` or `input`.                                                                               |
| `options`        | For `select` | At least 2 choices. The MCP `ask_user` tool accepts 2 to 6, so keep to 6 if you use both. `value` is the chosen option. |
| `placeholder`    | No           | Hint text for an `input` question. `value` is the typed text, up to 5,000 characters.                                   |
| `context`        | No           | Background shown above the question.                                                                                    |
| `agentName`      | No           | Shown in the push title as "Marketing agent is asking".                                                                 |
| `sessionId`      | No           | Lets a kill switch for this session stop the ask.                                                                       |
| `wait`           | No           | `true` (default) blocks. `false` returns at once.                                                                       |
| `timeoutSeconds` | No           | How long the call blocks. Default 30, maximum 55.                                                                       |
| `callbackUrl`    | No           | HTTPS URL that receives the answer. See the webhook section below.                                                      |

## Ask without blocking, then poll [#ask-without-blocking-then-poll]

Set `"wait": false` and the call returns at once with `"status": "pending"` and a `pollUrl`:

```json
{ "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "status": "pending", "answered": false, "pollUrl": "https://pushary.com/api/v1/server/ask/a1b2c3d4-e5f6-7890-abcd-ef1234567890" }
```

Poll it with `GET`. Add `?wait=30` to hold the request open for up to 30 seconds (maximum 55):

```bash
curl "https://pushary.com/api/v1/server/ask/a1b2c3d4-e5f6-7890-abcd-ef1234567890?wait=30" \
  -H "Authorization: Bearer $PUSHARY_API_KEY"
```

`status` is `pending`, `answered`, `expired` or `cancelled`. An unknown ID returns 404.

Cancel a question you no longer need:

```bash
curl -X DELETE "https://pushary.com/api/v1/server/ask/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Authorization: Bearer $PUSHARY_API_KEY"
```

The response says `"cancelled": true` when a pending question was removed. `false` means it was already answered, expired or cancelled.

## Get the answer on a webhook [#get-the-answer-on-a-webhook]

Use a webhook when your process cannot wait, for example an n8n or Make workflow. In n8n or Make, pass the URL of a node that waits for a webhook as `callbackUrl`. The workflow resumes when the answer arrives.

<Steps>
  <Step>
    Fetch your signing secret once. The first call creates it:

    ```bash
    curl https://pushary.com/api/v1/server/webhook-secret \
      -H "Authorization: Bearer $PUSHARY_API_KEY"
    ```

    It returns `{ "webhookSecret": "whsec_..." }`. A `POST` to the same URL rotates it. Do this before your first webhook ask, or the callback is signed with a secret you cannot check.
  </Step>

  <Step>
    Add `callbackUrl` to the ask. It must be HTTPS and must not point at localhost or a private network address:

    ```bash
    curl -X POST https://pushary.com/api/v1/server/ask \
      -H "Authorization: Bearer $PUSHARY_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "question": "Email the refund confirmation to the customer?",
        "wait": false,
        "callbackUrl": "https://your-app.com/webhooks/pushary"
      }'
    ```
  </Step>

  <Step>
    Verify each callback. When you answer, Pushary sends a `POST` with this body:

    ```json
    {
      "correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "answer": "yes",
      "value": "yes",
      "answeredAt": "2026-10-04T12:00:00.000Z",
      "context": "Only present when you passed context"
    }
    ```

    The `X-Pushary-Signature` header is the hex HMAC-SHA256 of the raw request body, keyed with your webhook secret:

    ```ts
    import crypto from 'node:crypto'

    const isFromPushary = (rawBody: string, signature: string, secret: string): boolean => {
      const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
      return expected.length === signature.length
        && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
    }
    ```
  </Step>
</Steps>

The callback fires only when someone answers, never on a timeout or a cancel. Return a 2xx status within 10 seconds. Pushary does not follow redirects, and it retries a failed delivery up to 12 attempts in total, so handle the same `correlationId` twice safely.

<Callout type="info">
  The REST API and the MCP server share one question store. An answer from your phone, the Mac app, the web page or Slack settles the question however it was asked.
</Callout>

## Send a one-way notification [#send-a-one-way-notification]

The MCP `send_notification` tool reaches your phone app and your browser. You can call it over plain HTTP, with no MCP library:

```bash
curl -X POST https://pushary.com/api/mcp/mcp \
  -H "Authorization: Bearer $PUSHARY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"send_notification","arguments":{"title":"Backfill finished","body":"Processed 12,400 rows with 0 errors."}}}'
```

`title` takes up to 100 characters and `body` up to 500. The limit is 10 notifications per 5 minutes per site.

`POST /api/v1/server/send` reaches **browser push subscribers only**. It does not reach the Pushary phone app. If nobody has subscribed in a browser, it returns 400 `No subscribers found matching criteria`.

## Connect an MCP client [#connect-an-mcp-client]

Point any MCP client at the Pushary endpoint. The agent then has `ask_user`, `wait_for_answer`, `cancel_question`, `send_notification` and `propose_scope` as tools.

```json title="mcp.json"
{
  "mcpServers": {
    "pushary": {
      "url": "https://pushary.com/api/mcp/mcp",
      "headers": {
        "Authorization": "Bearer ${PUSHARY_API_KEY}"
      }
    }
  }
}
```

The official MCP SDKs for TypeScript and Python connect with only the URL and the `Authorization` header. A custom agent, or a LangChain or LangGraph tool, can use the same settings. For the approval patterns, see [Human in the loop](/docs/agents/human-in-the-loop).

### Cline [#cline]

Run `npx pushary@latest setup --agents cline`. It adds Pushary to Cline's MCP settings with your key, and a global rule that tells Cline to ask and notify through Pushary.

To add it by hand, edit `~/.cline/data/settings/cline_mcp_settings.json`, the file Cline 4 reads for both the VS Code extension and the Cline CLI. Cline does not fill in `${PUSHARY_API_KEY}`, so paste the key itself. Keep `"type": "streamableHttp"`: without it Cline falls back to the older SSE transport and cannot connect to this URL.

```json title="~/.cline/data/settings/cline_mcp_settings.json"
{
  "mcpServers": {
    "pushary": {
      "type": "streamableHttp",
      "url": "https://pushary.com/api/mcp/mcp",
      "headers": {
        "Authorization": "Bearer pk_your_key"
      },
      "timeout": 90
    }
  }
}
```

Then reload VS Code (Developer: Reload Window). Cline asks before each MCP tool call unless **Use MCP servers** is ticked in its Auto-approve menu. That setting covers every MCP server in Cline, not only Pushary, so tick it only if you are happy for all of them to run without a click.

## Name your agent [#name-your-agent]

Pass `agentName` (and `sessionId` if you have one) on every call. The dashboard and the Fleet Board then label the work with your agent's name instead of a generic session. Several agents running at once each show up on their own.

## Errors and limits [#errors-and-limits]

| Response                                                 | Cause                                                                                                                                                                                          |
| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 400                                                      | `question` missing or over 8000 characters, a `select` with fewer than 2 options, or a `callbackUrl` that is not HTTPS or points at a private host.                                            |
| 401                                                      | The API key is missing, wrong or revoked.                                                                                                                                                      |
| 403 `forbidden`, `This key cannot access this endpoint.` | The key cannot call this endpoint. A key from setup or phone pairing cannot call the subscriber list, campaign, template or flow endpoints. Create a key in **Settings > API keys** for those. |
| 403 `subscription_required`                              | The workspace has no active plan or trial.                                                                                                                                                     |
| 403 `partner_decision_required`                          | Partner plan workspaces use `POST /api/v1/server/decisions` instead.                                                                                                                           |
| 429                                                      | 25 questions are already pending for this site, or you hit your plan's request limit.                                                                                                          |

If your approval mode is In agent, `/ask` sends no push and the response says `"mode": "terminal_only"`. To get every question on your phone, open the Pushary app, tap **Questions & approvals** at the top of your inbox, and pick **Always**. See [Permission policies](/docs/agents/policies) for all four modes.

## Next steps [#next-steps]

<Cards>
  <Card title="Ask for approval the safe way" description="Read the answer, handle a question nobody answered, and cancel stale questions." href="/docs/agents/human-in-the-loop" />

  <Card title="Copy a ready-made prompt" description="Prompts and code for alerts, approvals and backend notifications." href="/docs/agents/use-cases" />

  <Card title="Read the tools reference" description="Every MCP tool and argument." href="/docs/agents/reference/tools" />
</Cards>