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

# Monitoring a run

> Status, the event log, results, and agent-run webhooks.

export const OutcrawlStyles = () => {
  if (typeof document !== 'undefined' && !document.getElementById('outcrawl-overrides')) {
    const s = document.createElement('style');
    s.id = 'outcrawl-overrides';
    s.textContent = `
      /* Match the product's radius grid (4 / 6 / 8 / 9999), not Mintlify's default. */
      .rounded-sm { border-radius: 0.375rem !important; } /* 6px, --r-ctl */
      .rounded-md { border-radius: 0.375rem !important; } /* 6px, --r-ctl */
      .rounded-lg { border-radius: 0.5rem !important; }   /* 8px, --r-panel */
      .rounded-l-sm { border-top-left-radius: 0.375rem !important; border-bottom-left-radius: 0.375rem !important; }
      .rounded-r-sm { border-top-right-radius: 0.375rem !important; border-bottom-right-radius: 0.375rem !important; }
      .rounded-l-md { border-top-left-radius: 0.375rem !important; border-bottom-left-radius: 0.375rem !important; }
      .rounded-r-md { border-top-right-radius: 0.375rem !important; border-bottom-right-radius: 0.375rem !important; }
      .rounded-l-lg { border-top-left-radius: 0.5rem !important; border-bottom-left-radius: 0.5rem !important; }
      .rounded-r-lg { border-top-right-radius: 0.5rem !important; border-bottom-right-radius: 0.5rem !important; }

      /* Cards: never pure white in light mode, matching the one-big-card surface tone. */
      .card { background-color: #fcfcfc !important; border-radius: 0.5rem !important; }
      html.dark .card { background-color: #141311 !important; }

      /* Docs hero box */
      .oc-hero { background-color: #fcfcfc; border: 1px solid #e5e2dc; }
      html.dark .oc-hero { background-color: #141311; border-color: #26241f; }
      html.dark .oc-hero h1 { color: #edeae4; }

      /* Thin, transparent-track, hide-until-hover scrollbar. */
      ::-webkit-scrollbar { width: 6px; height: 6px; background-color: transparent; }
      ::-webkit-scrollbar-track { background-color: transparent; }
      ::-webkit-scrollbar-thumb { background-color: rgba(155, 155, 155, 0.5); border-radius: 10px; transition: opacity 0.3s ease; opacity: 0; }
      ::-webkit-scrollbar-thumb:hover { background-color: rgba(155, 155, 155, 0.7); }
      *:hover::-webkit-scrollbar-thumb,
      *:focus::-webkit-scrollbar-thumb,
      *:active::-webkit-scrollbar-thumb { opacity: 1; }
      * { scrollbar-width: thin; scrollbar-color: rgba(155, 155, 155, 0.5) transparent; }
    `;
    document.head.appendChild(s);
  }
  return null;
};

<OutcrawlStyles />

## Status

<CodeGroup>
  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
  import { Outcrawl } from '@outcrawl/sdk';

  const oc = new Outcrawl();
  const run = await oc.agent.get('<id>');
  console.log(run.status, run.goals, run.credits, run.data);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
  import asyncio
  from outcrawl import Outcrawl

  async def main():
      async with Outcrawl() as oc:
          run = await oc.agent.get("<id>")
          print(run["status"], run.get("goals"), run["credits"], run.get("data"))

  asyncio.run(main())
  ```

  ```bash curl theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
  curl https://api.outcrawl.ai/v1/agent/<id> \
    -H "Authorization: Bearer ock_YOUR_KEY_ID.YOUR_SECRET"
  ```
</CodeGroup>

Where a run has got to: status, the plan it stated, spend so far, and its answer once it has one. Safe to poll often, it is a row read, not a browser. A job you submitted in the same process has the same call as `job.status()`.

## Events

<CodeGroup>
  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
  import { Outcrawl } from '@outcrawl/sdk';

  const oc = new Outcrawl();
  const events = oc.agent.events('<id>', { after: 0, level: 'step' });
  for await (const record of events) {
    console.log(record.seq, record.kind, record.value);
  }
  console.log(events.cursor); // pass back as `after` to resume from here
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
  import asyncio
  from outcrawl import Outcrawl

  async def main():
      async with Outcrawl() as oc:
          events = oc.agent.events("<id>", after=0, level="step")
          async for record in events:
              print(record["seq"], record["kind"], record["value"])
          print(events.cursor)  # pass back as `after` to resume from here

  asyncio.run(main())
  ```

  ```bash curl theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
  curl "https://api.outcrawl.ai/v1/agent/<id>/events?after=<cursor>&level=step" \
    -H "Authorization: Bearer ock_YOUR_KEY_ID.YOUR_SECRET"
  ```
</CodeGroup>

Everything a run has done, in order: steps, findings, dead ends, plan changes. Ask for the verbosity you want with `level`: `status`, `step` (the default) or `debug`. Resume from the `after` cursor you last saw rather than re-reading from the start. Over HTTP this streams as newline-delimited JSON; the SDKs page through it for you and keep going until the run ends.

## Results

<CodeGroup>
  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
  import { Outcrawl } from '@outcrawl/sdk';

  const oc = new Outcrawl();
  let cursor: string | undefined;
  do {
    const page = await oc.agent.results('<id>', { cursor });
    for (const record of page) {
      console.log(record.kind, record.value);
    }
    cursor = page.cursor;
  } while (cursor !== undefined);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
  import asyncio
  from outcrawl import Outcrawl

  async def main():
      async with Outcrawl() as oc:
          cursor = None
          while True:
              page = await oc.agent.results("<id>", cursor=cursor)
              for record in page:
                  print(record["kind"], record["value"])
              cursor = page.cursor
              if cursor is None:
                  break

  asyncio.run(main())
  ```

  ```bash curl theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
  curl "https://api.outcrawl.ai/v1/agent/<id>/results?cursor=<cursor>" \
    -H "Authorization: Bearer ock_YOUR_KEY_ID.YOUR_SECRET"
  ```
</CodeGroup>

The records a run has appended, oldest first. Records land during the run, so this answers before it finishes, and a cancelled run still delivers what it had already produced.

## Webhooks

A run can notify a bare webhook URL when it settles, and unlike a monitor, this one retries.

<CodeGroup>
  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
  import { Outcrawl } from '@outcrawl/sdk';

  const oc = new Outcrawl();
  const job = oc.agent({
    task: 'Report the title of the top story on Hacker News.',
    caps: { budget: '16.00' },
    webhook: 'https://yourapp.example/hooks/outcrawl',
  });
  console.log(await job.id());
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
  import asyncio
  from outcrawl import Outcrawl

  async def main():
      async with Outcrawl() as oc:
          job = oc.agent(
              task="Report the title of the top story on Hacker News.",
              caps={"budget": "16.00"},
              webhook="https://yourapp.example/hooks/outcrawl",
          )
          print(await job.id())

  asyncio.run(main())
  ```

  ```bash curl theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
  curl https://api.outcrawl.ai/v1/agent \
    -H "Authorization: Bearer ock_YOUR_KEY_ID.YOUR_SECRET" \
    -H "Content-Type: application/json" \
    -d '{
      "task": "Report the title of the top story on Hacker News.",
      "caps": { "budget": "16.00" },
      "webhook": "https://yourapp.example/hooks/outcrawl"
    }'
  ```

  ```text Signature header theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
  x-outcrawl-signature: t=1758000000,v1=<base64url hmac>
  ```
</CodeGroup>

Verify the signature before trusting a payload: the header carries a Unix timestamp and an HMAC, in the same `t=...,v1=...` shape Stripe's webhooks use.

<Note>
  An agent run can only notify a **bare webhook URL**, never Discord or Slack directly, those two are monitor-only targets. In exchange, a run's webhook **does retry**: up to 8 attempts, backing off 1 minute, 3 minutes, 9 minutes, 27 minutes, then three more at 1 hour each, about 3 hours 40 minutes in total, before it gives up after the eighth refusal. A monitor's notification, by contrast, is sent once. Either way, a run's own events stay readable through `agent.events` and `agent.results` regardless of whether the webhook ever lands.
</Note>


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