> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pomerado.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Call Pomerado from a backend

> Run a tool from your backend with the REST API, follow its job and receive a webhook.

Call the REST API from your backend with an API key. This guide runs a Google Flights search, follows its job and receives a webhook when the job finishes.

## Set up

Create an API key on the Dashboard under Settings, then API keys, and load it into `POMERADO_API_KEY` from your secret manager.

```ts theme={null}
const api = "https://api.pomerado.ai/v1";
const headers = {
  Authorization: `Bearer ${process.env.POMERADO_API_KEY}`,
  "Content-Type": "application/json",
};
```

## Find the tool

```ts theme={null}
const tools = await fetch(
  `${api}/tools?query=search+flights&site_url=google.com`,
  { headers },
);
const toolId = (await tools.json()).data[0].id;
```

## Run it

```ts theme={null}
const run = await fetch(`${api}/runs/read-only`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    tool_id: toolId,
    input: { origin: "SFO", destination: "JFK", departure_date: "2026-11-20" },
  }),
});
const { id: jobId } = await run.json(); // 202, with the job
```

For a write tool, use `POST /v1/runs` with an `Idempotency-Key` header, so a retry never acts on the site twice.

## Follow the job

Read the job until it finishes or asks a question, waiting `Retry-After` seconds between reads:

```ts theme={null}
async function follow(id: string) {
  for (;;) {
    const res = await fetch(`${api}/jobs/${id}`, { headers });
    const job = await res.json();
    if (job.status !== "queued" && job.status !== "running") return job;
    const seconds = Number(res.headers.get("retry-after") ?? 2);
    await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
  }
}

const job = await follow(jobId);
if (job.status === "succeeded") console.log(job.result);
```

A job with status `needs_input` waits on a person: send them its `input_request.answer_url`.

## Receive a webhook

Instead of polling, create a webhook once:

```bash theme={null}
curl https://api.pomerado.ai/v1/webhooks \
  -H "Authorization: Bearer $POMERADO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/pomerado/events", "events": ["job.needs_input", "job.succeeded", "job.failed"]}'
```

Store the `secret` in the answer; it's shown once. Verify each delivery with a Standard Webhooks library, answer with a `2xx` within 10 seconds, then read the job, since events never carry a result:

```ts theme={null}
import { Webhook } from "standardwebhooks";

const webhook = new Webhook(process.env.POMERADO_WEBHOOK_SECRET!);

app.post(
  "/pomerado/events",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    const event = webhook.verify(
      req.body,
      req.headers as Record<string, string>,
    ) as any;
    res.sendStatus(204);
    if (event.type === "job.succeeded") {
      const job = await (
        await fetch(`${api}/jobs/${event.data.job_id}`, { headers })
      ).json();
      console.log(job.result);
    }
  },
);
```

<Accordion title="Details">
  * A result is kept until its first read, and at most 60 minutes after the job ends. Only the API key that started the job can read it; `include_result=false` checks the job without using up the result.
  * An event can arrive more than once. Drop repeats by its `id`.
  * More in [webhooks](/guides/notifications/webhooks), [wait for a result](/guides/jobs/wait-for-a-result) and the [API reference](/api-reference/introduction).
</Accordion>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.