Banata

Reference

Webhooks

Use webhook callbacks to track long-running sandbox and AI agent work without holding open requests or relying on constant polling.

Webhooks are the best integration tool for long-running jobs.

They let your app react when important work finishes instead of:

  • keeping one request open
  • polling constantly
  • guessing whether the job is probably done

When you should use webhooks

Use webhooks when:

  • an AI agent task may run for a while
  • your backend should continue work only after Banata finishes
  • another system should receive a callback later

The normal webhook flow

  1. create a webhook endpoint in your own app
  2. register that endpoint with Banata
  3. queue agent work with promptAsync()
  4. pass your own metadata with the task
  5. let Banata notify you when the job finishes
  6. read files or download artifacts afterward

Create a webhook

ts
const webhook = await client.createWebhook({
  url: "https://your-app.example.com/banata/webhook",
  eventTypes: ["sandbox.agent.task.completed"],
  description: "Task completion receiver",
});

Exact response shape:

json
{
  "id": "kh7ev9z670rc9fk1gx7mqtdm09841zrj",
  "url": "https://your-app.example.com/banata/webhook",
  "description": "Task completion receiver",
  "eventTypes": [
    "sandbox.agent.task.completed"
  ],
  "enabled": true,
  "signingSecret": "2ca3941580ae81eb9769c91b31770b2646acbf0c41538a01",
  "createdAt": 1775038321611
}

Pass metadata with the task

ts
const queued = await sandbox.promptAsync(
  "Generate a report and save it to /workspace/report.pdf",
  {
    metadata: {
      userId: "user_123",
      jobId: "job_456",
      source: "customer-portal",
    },
  },
);

Useful event types

Common events you may want:

  • sandbox.created
  • sandbox.assigned
  • sandbox.ready
  • sandbox.paused
  • sandbox.resumed
  • sandbox.ended
  • sandbox.failed
  • sandbox.agent.ready
  • sandbox.agent.failed
  • sandbox.browser.ready
  • sandbox.browser.failed
  • sandbox.agent.task.queued
  • sandbox.agent.task.completed
  • sandbox.agent.task.failed
  • sandbox.checkpoint.created
  • sandbox.checkpoint.restored
  • sandbox.document.converted
  • sandbox.artifacts.updated
  • billing.subscription.updated
  • billing.plan.changed

Signatures and retries

Webhook deliveries are signed and retried automatically.

Your receiver should:

  • verify the signature
  • return 2xx when it accepts the event
  • make processing idempotent

Webhook payload shape

Every webhook uses the same top-level envelope:

json
{
  "id": "evt_123",
  "type": "sandbox.agent.task.completed",
  "createdAt": 1775035000000,
  "orgId": "org_123",
  "resource": {
    "type": "sandbox",
    "id": "k972syypfcfkwm4my46hgmj85n8403kg"
  },
  "data": {}
}

The part you usually inspect is data.

Handoff event example

If the AI asks a person to step in, the webhook includes the handoff reason and message.

This is the exact payload shape from a real delivery:

json
{
  "id": "69fea4ee-f8c5-460d-9b87-fcea6d0bd5ab",
  "type": "sandbox.handoff.requested",
  "createdAt": 1775038266864,
  "orgId": "org_test_1",
  "resource": {
    "type": "sandbox",
    "id": "k970q1ha95snk1chr8wkh4cg7h841vn7"
  },
  "data": {
    "sandboxId": "k970q1ha95snk1chr8wkh4cg7h841vn7",
    "state": "ready",
    "spriteName": "banata-sbx-system-warm--k970q1ha95snk1chr8wk",
    "region": null,
    "updatedAt": 1775038266864,
    "handoff": {
      "state": "requested",
      "reason": "custom",
      "message": "GitHub login is ready. Please take over.",
      "resumePrompt": "After the human finishes, continue from the current page and report the title and URL.",
      "requestedAt": 1775038266858
    }
  }
}

So if your app needs to react differently to:

  • captcha
  • mfa
  • approval
  • ambiguous_ui

you can do that directly from data.handoff.reason.

The delivery headers look like this:

http
X-Banata-Signature: t=1775038266975,v1=256f786610aa46db35456004b19a85440bb21ae0a131d73b6025013d6f0fc3cc
X-Banata-Timestamp: 1775038266975
X-Banata-Delivery-Attempt: 1
X-Banata-Event-Id: 69fea4ee-f8c5-460d-9b87-fcea6d0bd5ab
X-Banata-Event: sandbox.handoff.requested

Task completion event example

This is the most common async callback:

json
{
  "id": "evt_456",
  "type": "sandbox.agent.task.completed",
  "createdAt": 1775035000000,
  "orgId": "org_123",
  "resource": {
    "type": "sandbox",
    "id": "k972syypfcfkwm4my46hgmj85n8403kg"
  },
  "data": {
    "taskId": "kn76kdqsemvtzdxasavnn9cjds841ktj",
    "sessionId": "ses_2b7bb0e28ffelj1zzkufFqCT6x",
    "status": "completed",
    "metadata": {
      "userId": "user_123",
      "jobId": "job_456"
    },
    "result": {
      "sessionId": "ses_2b7bb0e28ffelj1zzkufFqCT6x"
    }
  }
}

This is why metadata is useful:

  • you send it with promptAsync()
  • you receive it back in the webhook
  • your app can map the completion to the correct user, job, or record

Webhooks vs direct reads

Use webhooks when:

  • the workflow is asynchronous
  • your app should react later

Use direct reads when:

  • you are debugging
  • you are building an admin view
  • you want a quick manual status check