How it works
Every tool already has an Input node that defines its data schema (see Tools overview). When the public form is enabled, Cargo hosts a zero-dependency SDK (@cargo-ai/form-sdk) that reads those input fields and renders them as a styled form on your page. Each submission runs the same workflow that the Trigger tab, plays, and agents run — only the entry point changes.
The tool must be published for the public form to accept submissions. The
form always renders the deployed version, not the working draft.
Enabling the public form
Declare the public form on the tool in code — it deploys withcargo-ai cdk deploy alongside everything else:
tools/enrich.ts
captchaSecret is a plain string in the config (not an encryption field), so
secret() doesn’t apply here — use env() to keep the value out of your
repo while still deploying it.publicForm config. After deploy, grab the embed snippet from the tool and drop it into your page.
Prefer the UI?
Prefer the UI?
Open your tool → Trigger tab → Public form → Configure, toggle
Enable public form on, set access / spam / appearance, and save. The
Embed snippet section then gives you a ready-to-paste
<script> tag.Access
Allowed origins
The form is gated by an explicit allow-list of browser origins. Requests from any other origin are rejected with403.
Requests without an
Origin header (server-to-server calls, curl) bypass the check.
Spam protection
Three layers run in order on every submission. Failing any one rejects the request with400.
Honeypot
A hidden decoy field is injected automatically by the SDK. Real users never see it; most spam bots fill every field they find. If it is filled, the submission is dropped. No configuration needed.Time-trap
The SDK stamps a render timestamp into the payload. Submissions arriving faster than Minimum fill time are rejected as bot traffic.CAPTCHA
When configured, Cargo verifies a CAPTCHA token server-side before running the workflow.
Configure both fields:
- Site key — public, embedded on your page
- Secret — server-side only, never sent to the browser
addHiddenFields:
Appearance
The SDK ships a sensible default stylesheet, but you can theme the form per-workspace so every embed picks up your brand automatically.
Embedders can still override any of these per page via the SDK’s
theme option — workspace defaults are merged with per-call overrides, and explicit overrides win.
In code, presentation.theme takes the full theme object — every key is required, and null means “use the SDK’s bundled default”:
Embedding the form
The easiest path is the CDN snippet from the Embed snippet section:<div>, and runs your tool’s workflow on submit.
The first few lines are a queueing stub: the CDN script loads with async, so it can finish loading before or after your inline code runs. The stub makes Cargo.loadForm safe to call immediately — calls are queued and replayed (and their promises resolved) as soon as the bundle loads. Don’t remove it, and don’t call Cargo.loadForm from an inline script without it.
npm
For SPA or framework projects:Headless mode
Bring your own UI and let the SDK handle validation, anti-spam metadata and submission:SDK reference
loadForm(toolUuid, options?, onReady?)
Loads the deployed schema and returns a FormInstance.
FormInstance lifecycle
Sync vs async submission
If a sync run exceeds 60s it falls back to async automatically —
onSuccess receives a { outcome: "pending", runUuid } response you can keep polling.
Privacy
The SDK is privacy-aware by default:- Respects Global Privacy Control (
Sec-GPC: 1) andDNT: 1. When the visitor has opted out, no anonymous id is set and UTM auto-capture is skipped. - Form submission is always an explicit user action, so opt-out never blocks the submission itself — only passive identity stitching is suppressed.
utm_source, utm_medium, utm_campaign, utm_term, utm_content and page_url from the current URL and includes them as hidden values on submit.
Best practices
Keep allowed origins tight
Keep allowed origins tight
Use
* only for widgets that genuinely run everywhere. For anything else,
list each host explicitly — it stops other sites from embedding your form
and burning your credits.Pair honeypot with CAPTCHA on high-value forms
Pair honeypot with CAPTCHA on high-value forms
Honeypot + time-trap catch nearly all unsophisticated bots for free. Add
Turnstile or hCaptcha when the form triggers credit-heavy work (AI nodes,
enrichment) or feeds downstream systems like a CRM.
Use async mode for long workflows
Use async mode for long workflows
Browsers won’t wait long. If your tool routinely takes more than a few
seconds (AI calls, multi-step enrichment), switch to
mode: "async" and
show a “we’ll be in touch” screen instead of a spinner.Test the embed on your real site
Test the embed on your real site
Origin / CORS issues only surface in a real browser on a real domain.
Always test the snippet on the page you’ll actually embed on, not just
localhost.
