MyBotBoxMyBotBox

API Prompt

Natural-language HTTP trigger for AI-first workflows.

The API Prompt trigger exposes an HTTP endpoint that accepts a natural-language prompt (plus optional structured context and metadata) and runs a workflow with those fields available as block outputs. It's the right trigger when you want your API to feel like "chat with a workflow" rather than "fill out this JSON schema."

Request contract

POST /api/webhooks/prompt/{path}
Content-Type: application/json

{
  "prompt": "Summarize these PDFs and email me the result",
  "context": { "pdf_ids": ["abc", "def"] },
  "metadata": { "request_origin": "internal-app-v2" }
}
  • prompt (string, required) — natural-language instruction. Capped at maxPromptLength (default 10 000 chars, hard limit 50 000).
  • context (object, optional) — arbitrary JSON. Use it to pass structured references (IDs, file paths, user profile) alongside the prompt.
  • metadata (object, optional) — caller-supplied identifiers you want surfaced for routing/logging.

Any additional top-level fields are ignored. Nested JSON inside context and metadata is preserved.

Block outputs

Available in downstream blocks via <apiprompt1.*>:

OutputTypeDescription
promptstringThe caller's prompt, trimmed + length-validated
contextjson | nullThe context object, or null if not provided
metadatajson | nullThe metadata object, or null if not provided
requestIdstringServer-generated ID — propagates into logs/traces
callerstringDerived caller identity (bearer-hash / header-hash / hmac-hash / IP)
timestampstringISO-8601 server receive time
guardrailsTriggeredbooleanTrue if any known prompt-injection pattern matched
matchedInjectionPatternsarrayNames of patterns that matched (empty if none)

Authentication

Supports the same 4 modes as the generic Webhook trigger:

ModeHeader sentGood for
nonePublic endpoints, internal tools behind another gateway
bearerAuthorization: Bearer <token>Programmatic AI callers — recommended default
custom_header<configured-name>: <token>Matching legacy conventions
hmac_sha256X-Signature: <hex> over raw bodyMaximum assurance — stateless, replay-protected

See Webhooks → Authentication for signing examples in bash / Node / Python.

Calling from a consumer — HMAC quickstart

When you configure HMAC-SHA256 authentication, the platform expects a hex digest of the exact raw bytes of your request body, signed with your shared secret, sent in the X-Signature header (or whatever name you set under Signature header).

The signature must cover the bytes you actually send on the wire. If your HTTP client re-serializes the JSON (re-orders keys, drops whitespace, escapes Unicode differently), the signature will not match. Build the body string once, sign that string, send that string. In curl, use --data-raw. In Python, pre-encode with json.dumps(...).encode() and pass data=body. In Node, build the string and pass it as the body of fetch.

curl

WEBHOOK_URL="https://mybotbox.com/api/webhooks/prompt/<path>"
SECRET="<your HMAC secret>"

BODY='{"prompt":"Summarize last week of standup notes","context":{"team":"platform"}}'
TIMESTAMP=$(date +%s)
SIGNATURE=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')

curl -X POST "$WEBHOOK_URL" \
  -H 'Content-Type: application/json' \
  -H "X-Signature: $SIGNATURE" \
  -H "X-Timestamp: $TIMESTAMP" \
  --data-raw "$BODY"

Notes:

  • X-Timestamp (Unix seconds, or ISO-8601) is required when the trigger's replay window is on (default 300s). Without it you get 401 — timestamp outside replay window.
  • sha256= prefix is accepted but not required — the server strips it case-insensitively.
  • X-Idempotency-Key: <your-request-id> is optional but recommended so retries return 200 { "status": "duplicate" } instead of re-firing the workflow.

Python

import hmac, hashlib, json, time, requests

WEBHOOK_URL = "https://mybotbox.com/api/webhooks/prompt/<path>"
SECRET = b"<your HMAC secret>"

body = json.dumps(
    {"prompt": "Summarize last week of standup notes", "context": {"team": "platform"}},
    separators=(",", ":"),
).encode()
sig = hmac.new(SECRET, body, hashlib.sha256).hexdigest()

resp = requests.post(
    WEBHOOK_URL,
    data=body,
    headers={
        "Content-Type": "application/json",
        "X-Signature": sig,
        "X-Timestamp": str(int(time.time())),
        "X-Idempotency-Key": "req-2026-05-17-001",
    },
)
print(resp.status_code, resp.text)

Node.js / TypeScript

import { createHmac } from 'node:crypto'

const WEBHOOK_URL = 'https://mybotbox.com/api/webhooks/prompt/<path>'
const SECRET = process.env.MBB_HMAC_SECRET! // never hardcode

const body = JSON.stringify({
  prompt: 'Summarize last week of standup notes',
  context: { team: 'platform' },
})
const sig = createHmac('sha256', SECRET).update(body).digest('hex')

const res = await fetch(WEBHOOK_URL, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Signature': sig,
    'X-Timestamp': Math.floor(Date.now() / 1000).toString(),
  },
  body,
})
console.log(res.status, await res.text())

Onboarding a consumer

When you give a third party access to one of your API Prompt triggers, share exactly four things — and nothing else:

  1. Webhook URLhttps://mybotbox.com/api/webhooks/prompt/<path>
  2. HMAC secret — the value you typed into the "HMAC secret" field
  3. Signature header nameX-Signature (default) or whatever custom name you set
  4. Body contract — the shape { prompt: string, context?: object, metadata?: object }, plus your configured maxPromptLength and whether requireContext is on

How to share the secret safely

The HMAC secret authenticates every call. If it leaks, anyone can fire your workflow with arbitrary prompts. Treat it like a database password.

Don't:

  • Email or Slack it in plain text
  • Paste it into shared docs (Notion, Confluence, Google Docs)
  • Commit it to any repo, even private
  • Reuse the same secret across staging and production

Do, in order of preference:

  1. 1Password / Bitwarden shared item — scoped to the recipient's email, with an expiry. Most teams already have this.
  2. Encrypted message — Signal, or gpg --encrypt --recipient <them@…> then send the ciphertext over any channel.
  3. One-time-secret linkhttps://onetimesecret.com or a self-hosted equivalent. Single-view, auto-destruct after a few minutes.
  4. Read it aloud on a video call — the recipient pastes it into their own secret store live, then you both close the share. Nothing written down.

Operational hygiene

  • Generate a strong secret: openssl rand -hex 32 (256 bits). The 1234567890-style placeholders are fine for the first smoke test, but rotate before sharing externally.
  • Rotate every 90 days, or immediately if a consumer offboards. Update the secret in the trigger UI, save, then notify your consumer. Old signatures stop working at the moment you save — if you need a grace window, run two triggers in parallel during the cutover.
  • Tell consumers where to put it: their secret manager (AWS Secrets Manager / GCP Secret Manager / HashiCorp Vault / Doppler) or their CI env-var store. Never in source.
  • Watch the logs after rotation: a sudden burst of 401s usually means a consumer missed the rotation memo.

The URL the trigger UI displays today is the canonical endpoint: /api/webhooks/prompt/<path>. The older /api/webhooks/trigger/<path> form (which the UI emitted for every trigger type in earlier versions) still works for back-compat — both routes share the same HMAC verification + safeguards. Prefer the /prompt/ form when documenting integrations.

Replay protection, idempotency, and rate limiting

  • Replay window (HMAC mode only): reject requests where X-Timestamp is more than N seconds from server time. Default 300s, 0 disables.
  • Idempotency: when enabled, duplicate requests (matched on X-Idempotency-Key, body id, or body hash) return 200 { status: "duplicate" } without re-running the workflow. 7-day dedup window.
  • Per-caller rate limit: default 20 requests/min, keyed by the derived caller identity. Triggers 429 with Retry-After header when exceeded.

Prompt-injection guardrails

When enabled (default on), the platform scans the prompt against a small deny-list of common injection patterns (ignore previous instructions, system: prefix, chat-template markers, etc.) and surfaces the result as guardrailsTriggered + matchedInjectionPatterns. The guardrails are non-blocking — the workflow still runs — so you can decide per workflow whether to refuse, route to human review, or proceed.

Guardrails are a best-effort prefilter, not a replacement for LLM-side defenses. Always treat the prompt as untrusted input in downstream blocks. For high-risk operations (sending email, executing code, calling paid APIs), add explicit confirmation steps or route to review when guardrailsTriggered is true.

Typical workflow shape

  1. API Prompt trigger (this block) — caller posts { prompt, context }.
  2. Agent / LLM block — receives <apiprompt1.prompt> and <apiprompt1.context>, plans next actions using tool-calling.
  3. Downstream action blocks (email, database, third-party APIs) — execute the plan.
  4. Response block — returns the agent's summary to the original caller.

Example — PDF summarizer

curl -X POST https://mybotbox.com/api/webhooks/prompt/abc123 \
  -H 'Authorization: Bearer $MBB_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": "Summarize these PDFs into bullet points and email to team@example.com",
    "context": {
      "pdf_gcs_paths": [
        "gs://my-bucket/docs/report-2026-q1.pdf",
        "gs://my-bucket/docs/report-2026-q2.pdf"
      ]
    }
  }'

Workflow setup:

  • API Prompt block → Agent (Claude Haiku) block with tools [gcs_read_file, gmail_send] and system prompt "Follow the user's instructions. Use tools as needed.".
  • Agent's user message: <apiprompt1.prompt>\n\nContext: <apiprompt1.context>.

Verify with the bundled scripts

# Bearer token mode (recommended default).
bun apps/sat/tests/staging/triggers/api_prompt/verify.ts \
  https://mybotbox.com/api/webhooks/prompt/<path> \
  --secret=<bearer-token>

# Or HMAC mode.
bun apps/sat/tests/staging/triggers/api_prompt/verify.ts \
  https://mybotbox.com/api/webhooks/prompt/<path> \
  --hmac=<hmac-secret>
python3 apps/sat/tests/staging/triggers/api_prompt/verify.py \
  https://mybotbox.com/api/webhooks/prompt/<path> \
  --secret=<bearer-token>

Source: apps/sat/tests/staging/triggers/api_prompt/.

When to use API Prompt vs Generic Webhook

Use API Prompt when…Use Generic Webhook when…
Body is a prompt + contextBody is structured JSON with fixed fields
First block is an LLM/agent that plansFirst block is a Condition / Database action
Caller is an AI system or chatbotCaller is a service posting events
You want guardrails + per-caller rate limits out of the boxYou want provider-style HMAC + dedupe

Generic Webhook is simpler and faster when the body fields map 1:1 to workflow variables. Use API Prompt when you want the LLM to be the router.