# Cube > Agent-native forms. People fill the page; agents fill it without opening it; you approve what agents send. Cube forms have one link for people and agents. A person fills the page; an agent reads the form as Markdown or JSON Schema and submits it over HTTP or MCP, without a browser. Answers an agent sends wait for the form owner to approve them. ## Forms - [Cube early access form](https://ui.usecube.io/f/early-access.md): how an agent fills it (HTTP, MCP, or a prefilled link for a chatbot). JSON Schema: https://ui.usecube.io/f/early-access.json - [Senior Product Engineer form](https://ui.usecube.io/f/job-application.md): how an agent fills it (HTTP, MCP, or a prefilled link for a chatbot). JSON Schema: https://ui.usecube.io/f/job-application.json - [Agents in Production: SF meetup form](https://ui.usecube.io/f/event-registration.md): how an agent fills it (HTTP, MCP, or a prefilled link for a chatbot). JSON Schema: https://ui.usecube.io/f/event-registration.json - [New client intake form](https://ui.usecube.io/f/client-intake.md): how an agent fills it (HTTP, MCP, or a prefilled link for a chatbot). JSON Schema: https://ui.usecube.io/f/client-intake.json - [How are we doing? form](https://ui.usecube.io/f/feedback.md): how an agent fills it (HTTP, MCP, or a prefilled link for a chatbot). JSON Schema: https://ui.usecube.io/f/feedback.json - [Talk to sales form](https://ui.usecube.io/f/talk-to-sales.md): how an agent fills it (HTTP, MCP, or a prefilled link for a chatbot). JSON Schema: https://ui.usecube.io/f/talk-to-sales.json - [Report a bug form](https://ui.usecube.io/f/bug-report.md): how an agent fills it (HTTP, MCP, or a prefilled link for a chatbot). JSON Schema: https://ui.usecube.io/f/bug-report.json - [Make a form](https://ui.usecube.io/new.md): how an agent makes a new form for its person: the form as JSON, then a link they open in the builder to publish it (or the MCP tool `draft_form`). ## Docs - [Forms for agents](https://ui.usecube.io/docs/forms.md): How an agent fills a Cube form: Markdown, JSON Schema, HTTP, MCP, WebMCP, files, or a prefilled link for a chatbot. - [Receiving answers](https://ui.usecube.io/docs/forms/receiving-answers.md): For a form owner: send answers to email, a signed webhook or Slack, and verify Cube-Signature. ## Optional - [Full text](https://ui.usecube.io/llms-full.txt): this index and every page together. --- # Forms for agents > How an agent fills a Cube form: read it as Markdown or JSON, submit over HTTP or MCP, use it in a browser with WebMCP, or hand a person a prefilled link. A Cube form is one link. Anyone signed in can make one at `/forms/new` with a drag-and-drop builder (21 field types), or have their agent make it: [Make a form](https://ui.usecube.io/new.md) tells an agent how. A person fills the page. An agent fills the same link without opening a browser. What an agent sends waits for the form's owner to approve it; what a person sends counts at once. The examples use the early-access form, [https://ui.usecube.io/f/early-access](https://ui.usecube.io/f/early-access). ## Read the form Ask for the form's address without asking for HTML and you get Markdown: what the form is for, each field (name, type, required, description) and exactly how to submit it. ```sh curl https://ui.usecube.io/f/early-access ``` | Address | What you get | | --- | --- | | `/f/early-access` | The page for a browser (an `Accept` that names `text/html`); Markdown for anything else. `Vary: Accept`. | | `/f/early-access.md` | Always the Markdown. | | `/f/early-access.json` | The JSON Schema of the answers. | ## Submit over HTTP POST the answers as JSON to the same address. `agent` and `on_behalf_of` are optional; the owner approves faster when they can see who sent it. ```sh curl -X POST https://ui.usecube.io/f/early-access \ -H 'Content-Type: application/json' \ -d '{"answers": {"name": "Jane Doe", "email": "jane@example.com", "use": "Vendor intake forms."}, "agent": {"name": "Claude", "model": "claude-sonnet-4-5"}, "on_behalf_of": "Jane Doe"}' ``` A success is `201 Created`: ```json { "id": "s_mgk2x1a4f3c9", "status": "working", "metadata": { "review": "pending", "message": "Received and waiting for the form owner's approval. It counts once approved." } } ``` `status` uses [A2A](https://a2a-protocol.org)'s task states: `working` while it waits for the owner's Do or Skip, then `completed` (Do) or `rejected` (Skip). The reply also has a `receipt_url`, your person's own view of what was sent. A mistake is `400 Bad Request` with an `errors` list, each `{ "field", "message" }` written so a model can fix the answer and send again. `429 Too Many Requests` means wait the `Retry-After` seconds (ten submissions a minute from one address). ## Files A form can ask for a file (PDF, Word `.doc` or `.docx`, PNG, JPEG, WebP or GIF). The bytes are checked, not the name or the type you claim. At most 10 MB a file and 50 MB a submission. If the file is on the person's computer: an agent that can run commands POSTs it as multipart, so the file never passes through the agent. An agent that cannot calls `prefill_link` and gives the person the link; the person attaches the file on the page and presses Submit. A file can go in three ways; `resume` stands for the file question's name. 1. **Multipart.** Send `multipart/form-data`: the other answers as JSON in the part `answers` (`agent` and `on_behalf_of` are parts too), each file as a part named after its question. ```sh curl -X POST https://ui.usecube.io/f/ -F 'answers={"name": "Jane Doe"}' -F 'agent={"name": "Claude"}' -F 'resume=@resume.pdf' ``` 2. **Base64 in JSON.** The answer is an object. `name` and `base64` are required; `type` is optional. A `data:` prefix is accepted. Only for a small file the agent already holds (about 1 MB at most); do not read a file into the agent just to send it. ```json { "answers": { "resume": { "name": "resume.pdf", "type": "application/pdf", "base64": "JVBERi0xLjQK..." } } } ``` 3. **A link the server fetches.** The answer is `{"url": "https://..."}` (`name` optional). Only `https`, no user or password in it, and the address must be public: private, loopback, link-local and other special addresses are refused. Redirects are followed only after the same check, at most 3. 10 seconds in all, at most 10 MB read. ```json { "answers": { "resume": { "url": "https://example.com/resume.pdf" } } } ``` A file that fails comes back as a `400` error on that field (too big, not a type the form takes, not base64, the link could not be fetched). Over 50 MB in all is `413`. Files are kept for the owner only; they are never served to the public. ## MCP `https://ui.usecube.io/mcp` is a Streamable HTTP MCP server with five tools: | Tool | What it does | | --- | --- | | `list_forms` | The site's own forms you can fill: id, title, what each is for. A form someone made is reached by its link, never listed. | | `read_form(id)` | One form's fields, the JSON Schema of its answers, `link_for_a_person`, and, for a form with a file, `files` with the exact curl command. The id is the last part of its `/f/` link. | | `submit_form(id, answers, on_behalf_of?, agent?)` | Submits the answers; held for the owner like any agent's. A file answer is `{"name", "type", "base64"}` or `{"url"}`. | | `prefill_link(id, answers)` | Sends nothing. Returns `url` (the form with the answers in the link), `filled` and `tell_your_person`; also `left_out`, `needs_fixing` and `still_required` when they apply. A file and a consent box are never in the link: the person attaches and ticks them on the page. | | `draft_form(title, description?, fields)` | Makes a new form for the person, and stores nothing. Returns `url` (the builder with the form in the link, where the person signs in and presses Publish), `questions` and `tell_your_person`; also `fixed` and `defaults` when they apply. The question types and their settings are in [Make a form](https://ui.usecube.io/new.md). | Add it to Claude Code: ```sh claude mcp add --transport http cube https://ui.usecube.io/mcp ``` Or to any client that takes a URL: ```json { "mcpServers": { "cube": { "url": "https://ui.usecube.io/mcp" } } } ``` ## WebMCP In a browser with [WebMCP](https://webmachinelearning.github.io/webmcp/), the page's form is a tool. It carries the declarative attributes (`toolname`, `tooldescription`, and `toolparamdescription` on each field), so an in-browser agent can fill and submit it, and when the imperative API is there (`document.modelContext`) the page also registers a `submit_form` tool. Where neither exists, nothing changes. ## A prefilled link, for a chatbot A chat assistant that cannot send requests or use MCP can still help: it gives its person a link with the answers in the fragment, each URL-encoded. ```text https://ui.usecube.io/f/early-access#name=...&email=...&use=...&website=... ``` The fragment never reaches the server. The page fills the fields from it, removes it from the address bar so it is not shared by accident, and says "Filled in by your assistant. Check and submit." The person checks the answers and presses Submit. It counts at once, and the owner sees that an assistant drafted it. ## Who sent it Every submission records how it came in (`page`, `prefill`, `http`, `mcp` or `webmcp`), the agent's name and model if it gave them, `on_behalf_of`, the user agent, a salted hash of the address, and any Web Bot Auth headers (`Signature-Agent`, `Signature-Input`) exactly as they arrived. The owner sees the agents' submissions as suggestions on the form's Answers sheet and presses Do or Skip on each. The owner can send answers on by email, webhook or Slack; see [Receiving answers](/docs/forms/receiving-answers). ## The early-access form | Field | Type | Required | Description | | --- | --- | --- | --- | | `name` | text | yes | The person's full name. | | `email` | email address | yes | The person's email address, where Cube will write to them. | | `use` | long text | yes | What the person would use agent-native forms for, in their own words. | | `website` | web address | no | The person's or their company's website, if they have one. | --- # Receiving answers > For a form's owner: send each answer to your email, a webhook or Slack, and check a webhook's Cube-Signature in Node. Make a form at `https://ui.usecube.io/forms/new` (sign in with an email code). Its page has three tabs: Answers, Share and Settings. Under Settings you choose where each answer goes. All three can be on at once. An answer a person sends goes out as it arrives. An answer an agent sends goes out only after you press Do on it; a Skip sends nothing. ## Email Enter an address. Cube sends it a confirmation first, and nothing else goes there until you press Confirm in that email (the link works once, for 24 hours). Then each answer arrives as an email with the answers, who sent them and a link back to Answers. ## Webhook Enter an `https` address. Cube shows the signing secret (`whsec_...`) once, when you connect; keep it. Each answer is a POST with a JSON body: ```json { "form": { "id": "f_abc", "title": "Vendor intake" }, "submission": { "id": "s_mgk2x1a4f3c9", "at": "2026-10-09T17:03:11.000Z", "channel": "mcp", "agent": { "name": "Claude", "model": "claude-sonnet-4-5" }, "on_behalf_of": "Jane Doe", "answers": { "name": "Jane Doe", "resume": { "name": "resume.pdf", "type": "application/pdf", "size": 48213, "url": "https://.../files/f_abc/?sig=..." } }, "files": [{ "name": "resume.pdf", "type": "application/pdf", "size": 48213, "url": "https://.../files/f_abc/?sig=..." }] }, "status": "completed" } ``` `channel` is `page`, `prefill`, `http`, `mcp` or `webmcp`. `agent` and `on_behalf_of` appear only when the sender gave them. A file's `url` is a signed link that works while the webhook stays connected. A test send from Settings sends the same shape with `"test": true`. Headers: `Cube-Signature: t=,v1=` and `Cube-Delivery: `. `v1` is the HMAC-SHA256, with your secret, of `.`; this is Stripe's scheme. Check it against the raw body, before parsing it: ```js import { createHmac, timingSafeEqual } from 'node:crypto' function verify(rawBody, header, secret, toleranceSeconds = 300) { const parts = Object.fromEntries(header.split(',').map((p) => p.split('='))) if (Math.abs(Date.now() / 1000 - Number(parts.t)) > toleranceSeconds) return false const expected = createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex') const a = Buffer.from(expected) const b = Buffer.from(parts.v1 ?? '') return a.length === b.length && timingSafeEqual(a, b) } ``` Answer with any `2xx` within 10 seconds; the body is not read. Redirects are not followed. A failed send is tried 3 times (right away, after 1 second, after 3 more). If the last try fails, the row says Failing with the reason, and the next success turns it back On. The address must be public `https`; private and loopback addresses are refused. Use `Cube-Delivery` to ignore a repeat. ## Slack Paste a Slack incoming webhook (`https://hooks.slack.com/services/...`). Each answer is a short message: the first four answers, who sent it and how, and an Open answers link. The address is never shown back. --- # Make a Cube form > How an agent makes a form for its person on Cube: write the form as JSON, then give your person a link that opens it in Cube's form builder. They look it over, sign in if they need to, and press Publish. Nothing is published until they do. In short: write the form as JSON (below), then give your person `https://ui.usecube.io/forms/new#spec=` followed by that JSON, URL-encoded. A Cube form is one link: people fill the page, and agents fill it without opening it. Once your person publishes it, the form is at `https://ui.usecube.io/f/` and its answers are theirs. ## 1. Write the form ```json { "title": "Pottery workshop RSVP", "description": "Saturday, 10am to noon. Clay and tools are provided.", "fields": [ { "type": "short_text", "label": "Your name", "required": true }, { "type": "email", "label": "Email", "required": true }, { "type": "single_select", "label": "Have you done pottery before?", "required": true, "options": [ "Never", "A few times", "Often" ] }, { "type": "long_text", "label": "Anything we should know?" } ] } ``` - `title`: the form's name, up to 200 characters. - `description` (optional): one line under the title for the people filling it in, up to 2,000 characters. - `fields`: the questions in order, at most 200. Each has: - `type`: one of the types below. - `label`: the question as the person reads it, up to 500 characters. - `required` (optional): `true` if it must be answered. Default `false`. - `help` (optional): a line under the question. - `description` (optional): one sentence telling an agent what to put, for agents that fill the form in later. Default: the label. - its type's settings (below), each optional. Anything else is ignored, and a question whose type isn't listed is left out. Each answer's key is made from its label. ## Question types | `type` | In the builder | What it is, and its settings (default) | | --- | --- | --- | | `short_text` | Short answer | `maxLength`: the most characters, up to 1,000 (200) | | `long_text` | Long answer | `maxLength`: the most characters, up to 10,000 (5000) | | `number` | Number | `min`: the smallest allowed; `max`: the largest allowed; `integer`: true for whole numbers only | | `money` | Amount | An amount and its currency. `defaultCurrency`: a three-letter currency code like "EUR" ("USD") | | `repeat` | Work history | Each entry is employer, title, start, end, current. `item`: what one entry is called ("entry") | | `single_select` | Multiple choice | `options`: the choices, up to 100 (["Option 1"]) | | `multi_select` | Checkboxes | `options`: the choices, up to 100 (["Option 1"]) | | `multi_select_other` | Checkboxes with Other | Checkboxes, and a line after "Other" for their own answer. `options`: the choices, up to 100 (["Option 1"]) | | `yes_no` | Yes / no | None | | `checkbox` | Consent | One box to tick. A required one is a consent: only the person ticks it. | | `demographic` | Self-identification | A voluntary question (gender, ethnicity...); "I don't wish to answer" is always added. `options`: the choices, up to 100 (["Option 1"]) | | `email` | Email | None | | `phone` | Phone number | `defaultCountry`: the country shown first, a two-letter code like "GB" ("US") | | `url` | Link | `site`: "linkedin" or "github" to ask for that profile | | `location` | City | None | | `address` | Address | None | | `date` | Date | `precision`: "month" for a year and month only | | `slot` | Time slot | `timeZone`: an IANA time zone like "Europe/London" ("America/Los_Angeles"); `zoneName`: that zone in words, like "London time" ("Pacific Time"); `weekdays`: the days offered, 0 = Sunday to 6 = Saturday ([1,2,3,4,5]); `times`: the start times offered, "HH:MM" in 24 hours, up to 48 (["09:00","10:00","11:00","13:00","14:00","15:00","16:00"]); `daysAhead`: how many days ahead it offers, 1 to 90 (14) | | `file` | File upload | .pdf, .doc, .docx, .png, .jpg, .webp, .gif, up to 10 MB; the person attaches it on the page. | | `rating` | Rating (NPS) | Net Promoter Score, 0 to 10. `low`: the words for 0 ("Not at all likely"); `high`: the words for 10 ("Extremely likely") | | `scale` | Linear scale | `min`: 0 or 1 (1); `max`: the top, up to 10 (5); `low`: the words for the bottom ("Not at all"); `high`: the words for the top ("Very much") | ## 2. Give your person the link **Any agent, even a chat assistant that can only write text:** the link is `https://ui.usecube.io/forms/new#spec=` followed by the form's JSON, URL-encoded (JavaScript's `encodeURIComponent`). If you can run code: `"https://ui.usecube.io/forms/new#spec=" + encodeURIComponent(JSON.stringify(form))`. Give it to your person as it is, in one piece. The form above is: https://ui.usecube.io/forms/new#spec=%7B%22title%22%3A%22Pottery%20workshop%20RSVP%22%2C%22description%22%3A%22Saturday%2C%2010am%20to%20noon.%20Clay%20and%20tools%20are%20provided.%22%2C%22fields%22%3A%5B%7B%22type%22%3A%22short_text%22%2C%22label%22%3A%22Your%20name%22%2C%22required%22%3Atrue%7D%2C%7B%22type%22%3A%22email%22%2C%22label%22%3A%22Email%22%2C%22required%22%3Atrue%7D%2C%7B%22type%22%3A%22single_select%22%2C%22label%22%3A%22Have%20you%20done%20pottery%20before%3F%22%2C%22required%22%3Atrue%2C%22options%22%3A%5B%22Never%22%2C%22A%20few%20times%22%2C%22Often%22%5D%7D%2C%7B%22type%22%3A%22long_text%22%2C%22label%22%3A%22Anything%20we%20should%20know%3F%22%7D%5D%7D Opening it puts the form in Cube's builder: your person checks the questions, changes anything they like, signs in if asked, and presses Publish. The part after `#` never reaches a server. Keep the link under 64,000 characters. **With the Cube MCP connector** (`https://ui.usecube.io/mcp`): call `draft_form` with `title`, `description` and `fields`. It checks the form the way the builder will and returns the same link (`url`), what it fixed (`fixed`) and the settings it filled in (`defaults`). You can't publish a form yourself: that needs your person's sign-in. Tell them to open the link, check the form and press Publish. --- # Cube early access > Sign a person up for early access to Cube's agent-native forms (forms people fill on the page and AI agents fill over HTTP or MCP, with the owner approving what agents send). Agent-native forms: people fill the page, agents fill it without opening it. This is a form you can fill in for a person without opening a browser. Its fields are `name`, `email`, `use`, `website` (`name`, `email`, `use` required). Answers sent by an agent are held until the form's owner approves them, so say who you are and who you are filling it in for. **Only send what your person told you. Ask them for any required answer you do not have, and never tick a consent box or answer a demographic question for them unless they told you to.** ## How to fill it 1. **If you can send HTTP requests:** `POST https://ui.usecube.io/f/early-access` with a JSON body `{"answers": {...}, "agent": {"name": "...", "model": "..."}, "on_behalf_of": "..."}`. Details below. 2. **If you have the Cube MCP connector** (`https://ui.usecube.io/mcp`): call `submit_form` with `id: "early-access"`, `answers` and `on_behalf_of`; or `prefill_link` with `id` and `answers` to get the link for your person (a file cannot ride in it; they attach it on the page). 3. **If you can't do either (a chatbot):** give your person this link with the answers filled in, each one URL-encoded (lists and objects as JSON). They check it and press Submit themselves: ``` https://ui.usecube.io/f/early-access#name=...&email=...&use=...&website=... ``` For example: https://ui.usecube.io/f/early-access#name=Jane%20Doe&email=jane%40example.com&use=...&website=https%3A%2F%2Fexample.com ## Fields - **name** (string, up to 200 characters, required): The person's full name. - **email** (string, email, required): The person's email address, where Cube will write to them. - **use** (string, up to 5,000 characters, required): What the person would use agent-native forms for, in their own words. A sentence or two is plenty. - **website** (string, URL): The person's or their company's website, if they have one. ## Submit over HTTP `POST https://ui.usecube.io/f/early-access` with `Content-Type: application/json`: ```json { "answers": { "name": "Jane Doe", "email": "jane@example.com", "use": "...", "website": "https://example.com" }, "agent": { "name": "Claude", "model": "claude-sonnet-4-5" }, "on_behalf_of": "Jane Doe" } ``` `answers` is required. `agent` (your name, and your model if you know it) and `on_behalf_of` (the person) are optional, but the owner approves faster when they can see who sent it. ```sh curl -X POST https://ui.usecube.io/f/early-access \ -H 'Content-Type: application/json' \ -d '{"answers":{"name":"Jane Doe","email":"jane@example.com","use":"...","website":"https://example.com"},"agent":{"name":"Claude","model":"claude-sonnet-4-5"},"on_behalf_of":"Jane Doe"}' ``` A success is `201 Created`: ```json { "id": "s_...", "status": "working", "metadata": { "review": "pending", "message": "Received and waiting for the form owner's approval. It counts once approved." }, "receipt_url": "https://ui.usecube.io/s/..." } ``` `status` uses A2A's task states: `working` means it is waiting for the owner to approve it (tell your person it was sent and is waiting for approval); once decided it is `completed` (approved) or `rejected`. `receipt_url` is your person's own view of what was sent; give it to them. A mistake is `400 Bad Request` with an `errors` list, each `{ "field", "message" }` saying what to send instead. Fix those answers and send again. `429 Too Many Requests` means wait the `Retry-After` seconds. ## Other formats - [JSON Schema of the answers](https://ui.usecube.io/f/early-access.json) - [This page as Markdown](https://ui.usecube.io/f/early-access.md) - MCP (Streamable HTTP): `https://ui.usecube.io/mcp`, tools `list_forms`, `read_form`, `submit_form`, `prefill_link`, `draft_form`. - In a browser with WebMCP, the page's form is the tool `sign_up_for_cube_early_access`. --- # Senior Product Engineer > Apply for the Senior Product Engineer role at Lumen Labs (a demo job application). Lumen Labs · San Francisco or remote. Tell us about you and what you’ve built. This is a form you can fill in for a person without opening a browser. Its fields are `first_name`, `last_name`, `email`, `phone`, `resume`, `cover_letter`, `linkedin`, `portfolio`, `location`, `work_authorization`, `sponsorship`, `salary`, `shipped`, `heard`, `proud`, `gender`, `race`, `veteran`, `disability`, `privacy` (`first_name`, `last_name`, `email`, `phone`, `resume`, `location`, `work_authorization`, `sponsorship`, `shipped`, `proud`, `privacy` required). Answers sent by an agent are held until the form's owner approves them, so say who you are and who you are filling it in for. **Only send what your person told you. Ask them for any required answer you do not have, and never tick a consent box or answer a demographic question for them unless they told you to.** ## How to fill it 1. **If you can send HTTP requests:** `POST https://ui.usecube.io/f/job-application` with a JSON body `{"answers": {...}, "agent": {"name": "...", "model": "..."}, "on_behalf_of": "..."}`. Details below. 2. **If you have the Cube MCP connector** (`https://ui.usecube.io/mcp`): call `submit_form` with `id: "job-application"`, `answers` and `on_behalf_of`; or `prefill_link` with `id` and `answers` to get the link for your person (a file cannot ride in it; they attach it on the page). 3. **If you can't do either (a chatbot):** give your person this link with the answers filled in, each one URL-encoded (lists and objects as JSON). They check it and press Submit themselves: ``` https://ui.usecube.io/f/job-application#first_name=...&last_name=...&email=...&phone=...&cover_letter=...&linkedin=...&portfolio=...&location=...&work_authorization=...&sponsorship=...&salary=...&shipped=...&heard=...&proud=... ``` For example: https://ui.usecube.io/f/job-application#first_name=Jane&last_name=Doe&email=jane%40example.com&phone=%2B14155550123&resume=%7B%22url%22%3A%22https%3A%2F%2Fexample.com%2Fresume.pdf%22%7D&location=%7B%22city%22%3A%22Seattle%22%2C%22region%22%3A%22Washington%22%2C%22country%22%3A%22United%20States%22%7D&shipped=%5B%22Web%20frontend%22%5D&proud=... ## Fields - **first_name** (string, up to 200 characters, required): The applicant's first name. - **last_name** (string, up to 200 characters, required): The applicant's last name. - **email** (string, email, required): Where the team reaches the applicant. - **phone** (string, E.164 phone number like +14155550123, required): A number the team can call or text. - **resume** (file, PDF or Word (.doc) file or Word file up to 10 MB, required): The applicant's resume. If the file is on your person's computer and you can run commands, POST it as multipart; if you cannot, call the MCP tool `prefill_link` (or build the link below) and give your person the link: they attach the file on the page and press Submit. A file goes in one of three ways: (1) a multipart/form-data POST with the part `resume` (and the other answers as JSON in the part `answers`); (2) in JSON as {"name": "resume.pdf", "type": "application/pdf", "base64": "..."}, only for a small file you already hold, never by reading a file into your context; (3) in JSON as {"url": "https://..."}, a public https link the form downloads. At most 10 MB a file, 50 MB in all; the bytes are checked, not the name. - **cover_letter** (string, up to 5,000 characters): A cover letter, pasted as text. Leave it out if the applicant has none. - **linkedin** (string, URL of a LinkedIn profile): The applicant's LinkedIn profile. - **portfolio** (string, URL): A link to work the applicant wants to show. - **location** (object {city, region, country}; region and country only when known, required): The city the applicant lives in. - **work_authorization** (boolean, required): Whether the applicant may legally work where the job is. - **sponsorship** (boolean, required): Whether the applicant needs, or will need, a work visa sponsored. - **salary** (object {amount: number, currency: one of "USD", "EUR", "GBP", "CAD"}): The yearly base salary the applicant expects, before tax. - **shipped** (array of strings, any of "Web frontend", "Backend APIs", "Mobile", "Data pipelines", "ML features", "None of these", required): The kinds of software the applicant has shipped to real users. - **heard** (string, one of "Company website", "LinkedIn", "Referral", "Job board", "Social media", "Other"): Where the applicant found the job. - **proud** (string, up to 1,500 characters, required): In their own words: one product they built and what they did on it. - **gender** (string, one of "Woman", "Man", "Non-binary", "I don't wish to answer"): Voluntary, for equal-opportunity reporting. Send only what the applicant chose to share; if they did not say, send "I don't wish to answer" or leave it out. - **race** (string, one of "Hispanic or Latino", "White", "Black or African American", "Asian", "Native Hawaiian or Other Pacific Islander", "American Indian or Alaska Native", "Two or more races", "I don't wish to answer"): Voluntary, for equal-opportunity reporting. Send only what the applicant chose to share; if they did not say, send "I don't wish to answer" or leave it out. - **veteran** (string, one of "I am a protected veteran", "I am not a protected veteran", "I don't wish to answer"): Voluntary, for equal-opportunity reporting. Send only what the applicant chose to share; if they did not say, send "I don't wish to answer" or leave it out. - **disability** (string, one of "Yes, I have a disability (or previously had one)", "No, I don't have a disability", "I don't wish to answer"): Voluntary, for equal-opportunity reporting. Send only what the applicant chose to share; if they did not say, send "I don't wish to answer" or leave it out. - **privacy** (boolean, must be true, required): The applicant has read the privacy notice. Set it only if they told you they have. Must be confirmed by the person; when submitting directly over HTTP or MCP, set it only if your person explicitly agreed. ## Submit over HTTP `POST https://ui.usecube.io/f/job-application` with `Content-Type: application/json`: ```json { "answers": { "first_name": "Jane", "last_name": "Doe", "email": "jane@example.com", "phone": "+14155550123", "resume": { "url": "https://example.com/resume.pdf" }, "location": { "city": "Seattle", "region": "Washington", "country": "United States" }, "shipped": [ "Web frontend" ], "proud": "..." }, "agent": { "name": "Claude", "model": "claude-sonnet-4-5" }, "on_behalf_of": "Jane Doe" } ``` `answers` is required. `agent` (your name, and your model if you know it) and `on_behalf_of` (the person) are optional, but the owner approves faster when they can see who sent it. ```sh curl -X POST https://ui.usecube.io/f/job-application \ -H 'Content-Type: application/json' \ -d '{"answers":{"first_name":"Jane","last_name":"Doe","email":"jane@example.com","phone":"+14155550123","resume":{"url":"https://example.com/resume.pdf"},"location":{"city":"Seattle","region":"Washington","country":"United States"},"shipped":["Web frontend"],"proud":"..."},"agent":{"name":"Claude","model":"claude-sonnet-4-5"},"on_behalf_of":"Jane Doe"}' ``` With a file, send multipart/form-data instead: the answers as JSON in the part `answers`, each file as a part named after its question: ```sh curl -X POST https://ui.usecube.io/f/job-application \ -F 'answers={"first_name":"Jane","last_name":"Doe","email":"jane@example.com","phone":"+14155550123","location":{"city":"Seattle","region":"Washington","country":"United States"},"shipped":["Web frontend"],"proud":"..."}' \ -F 'resume=@resume.pdf' \ -F 'agent={"name": "Claude"}' -F 'on_behalf_of=Jane Doe' ``` A success is `201 Created`: ```json { "id": "s_...", "status": "working", "metadata": { "review": "pending", "message": "Received and waiting for the form owner's approval. It counts once approved." }, "receipt_url": "https://ui.usecube.io/s/..." } ``` `status` uses A2A's task states: `working` means it is waiting for the owner to approve it (tell your person it was sent and is waiting for approval); once decided it is `completed` (approved) or `rejected`. `receipt_url` is your person's own view of what was sent; give it to them. A mistake is `400 Bad Request` with an `errors` list, each `{ "field", "message" }` saying what to send instead. Fix those answers and send again. `429 Too Many Requests` means wait the `Retry-After` seconds. ## Other formats - [JSON Schema of the answers](https://ui.usecube.io/f/job-application.json) - [This page as Markdown](https://ui.usecube.io/f/job-application.md) - MCP (Streamable HTTP): `https://ui.usecube.io/mcp`, tools `list_forms`, `read_form`, `submit_form`, `prefill_link`, `draft_form`. - In a browser with WebMCP, the page's form is the tool `submit_job_application`. --- # Agents in Production: SF meetup > Register for the Agents in Production meetup in San Francisco (a demo event registration). Thursday, November 12, 6 PM · San Francisco. Tell us who’s coming. This is a form you can fill in for a person without opening a browser. Its fields are `full_name`, `email`, `phone`, `attendees`, `guest_names`, `dietary`, `comments` (`full_name`, `email`, `attendees` required). Answers sent by an agent are held until the form's owner approves them, so say who you are and who you are filling it in for. **Only send what your person told you. Ask them for any required answer you do not have, and never tick a consent box or answer a demographic question for them unless they told you to.** ## How to fill it 1. **If you can send HTTP requests:** `POST https://ui.usecube.io/f/event-registration` with a JSON body `{"answers": {...}, "agent": {"name": "...", "model": "..."}, "on_behalf_of": "..."}`. Details below. 2. **If you have the Cube MCP connector** (`https://ui.usecube.io/mcp`): call `submit_form` with `id: "event-registration"`, `answers` and `on_behalf_of`; or `prefill_link` with `id` and `answers` to get the link for your person (a file cannot ride in it; they attach it on the page). 3. **If you can't do either (a chatbot):** give your person this link with the answers filled in, each one URL-encoded (lists and objects as JSON). They check it and press Submit themselves: ``` https://ui.usecube.io/f/event-registration#full_name=...&email=...&phone=...&attendees=...&guest_names=...&dietary=...&comments=... ``` For example: https://ui.usecube.io/f/event-registration#full_name=Jane%20Doe&email=jane%40example.com&attendees=1 ## Fields - **full_name** (string, up to 200 characters, required): The attendee's full name. - **email** (string, email, required): Where the tickets go. - **phone** (string, E.164 phone number like +14155550123): A number for day-of updates. - **attendees** (integer from 1 to 5, required): How many people are coming, the attendee included. - **guest_names** (string, up to 1,000 characters): The names of the other people coming, if any. - **dietary** (object {choices: any of "Vegetarian", "Vegan", "Gluten-free", "Halal", "Kosher", "Nut allergy"; other: what they wrote after "Other", or null}): Any dietary needs in the group. - **comments** (string, up to 2,000 characters): Anything else the organizers should know. ## Submit over HTTP `POST https://ui.usecube.io/f/event-registration` with `Content-Type: application/json`: ```json { "answers": { "full_name": "Jane Doe", "email": "jane@example.com", "attendees": 1 }, "agent": { "name": "Claude", "model": "claude-sonnet-4-5" }, "on_behalf_of": "Jane Doe" } ``` `answers` is required. `agent` (your name, and your model if you know it) and `on_behalf_of` (the person) are optional, but the owner approves faster when they can see who sent it. ```sh curl -X POST https://ui.usecube.io/f/event-registration \ -H 'Content-Type: application/json' \ -d '{"answers":{"full_name":"Jane Doe","email":"jane@example.com","attendees":1},"agent":{"name":"Claude","model":"claude-sonnet-4-5"},"on_behalf_of":"Jane Doe"}' ``` A success is `201 Created`: ```json { "id": "s_...", "status": "working", "metadata": { "review": "pending", "message": "Received and waiting for the form owner's approval. It counts once approved." }, "receipt_url": "https://ui.usecube.io/s/..." } ``` `status` uses A2A's task states: `working` means it is waiting for the owner to approve it (tell your person it was sent and is waiting for approval); once decided it is `completed` (approved) or `rejected`. `receipt_url` is your person's own view of what was sent; give it to them. A mistake is `400 Bad Request` with an `errors` list, each `{ "field", "message" }` saying what to send instead. Fix those answers and send again. `429 Too Many Requests` means wait the `Retry-After` seconds. ## Other formats - [JSON Schema of the answers](https://ui.usecube.io/f/event-registration.json) - [This page as Markdown](https://ui.usecube.io/f/event-registration.md) - MCP (Streamable HTTP): `https://ui.usecube.io/mcp`, tools `list_forms`, `read_form`, `submit_form`, `prefill_link`, `draft_form`. - In a browser with WebMCP, the page's form is the tool `submit_event_registration`. --- # New client intake > Start working with a studio as a new client: contact details, company and project start (a demo client intake). A few details so we can prepare for our first call. This is a form you can fill in for a person without opening a browser. Its fields are `contact_name`, `email`, `phone`, `contact_method`, `company_address`, `company_website`, `project_start`, `documents` (`contact_name`, `email`, `phone`, `contact_method`, `company_address`, `company_website`, `project_start` required). Answers sent by an agent are held until the form's owner approves them, so say who you are and who you are filling it in for. **Only send what your person told you. Ask them for any required answer you do not have, and never tick a consent box or answer a demographic question for them unless they told you to.** ## How to fill it 1. **If you can send HTTP requests:** `POST https://ui.usecube.io/f/client-intake` with a JSON body `{"answers": {...}, "agent": {"name": "...", "model": "..."}, "on_behalf_of": "..."}`. Details below. 2. **If you have the Cube MCP connector** (`https://ui.usecube.io/mcp`): call `submit_form` with `id: "client-intake"`, `answers` and `on_behalf_of`; or `prefill_link` with `id` and `answers` to get the link for your person (a file cannot ride in it; they attach it on the page). 3. **If you can't do either (a chatbot):** give your person this link with the answers filled in, each one URL-encoded (lists and objects as JSON). They check it and press Submit themselves: ``` https://ui.usecube.io/f/client-intake#contact_name=...&email=...&phone=...&contact_method=...&company_address=...&company_website=...&project_start=... ``` For example: https://ui.usecube.io/f/client-intake#contact_name=Jane%20Doe&email=jane%40example.com&phone=%2B14155550123&contact_method=%5B%22Email%22%5D&company_address=%7B%22street%22%3A%221%20Main%20St%22%2C%22city%22%3A%22Seattle%22%2C%22region%22%3A%22WA%22%2C%22postal_code%22%3A%2298101%22%2C%22country%22%3A%22US%22%7D&company_website=https%3A%2F%2Fexample.com&project_start=2026-11-02 ## Fields - **contact_name** (string, up to 200 characters, required): The person the studio works with. - **email** (string, email, required): The contact's email address. - **phone** (string, E.164 phone number like +14155550123, required): The contact's phone number. - **contact_method** (array of strings, any of "Email", "Phone call", "Text message", "Video call", required): How the contact likes to be reached. - **company_address** (object {street, city, region, postal_code, country: ISO 3166-1 alpha-2 code like "US"}, required): The company's mailing address. - **company_website** (string, URL, required): The company's website. - **project_start** (string, ISO date YYYY-MM-DD, required): When the client wants the project to start. - **documents** (file, PDF or Word (.doc) file or Word file or PNG or JPEG or WebP or GIF up to 20 MB): A brief or any document the client wants to share. If the file is on your person's computer and you can run commands, POST it as multipart; if you cannot, call the MCP tool `prefill_link` (or build the link below) and give your person the link: they attach the file on the page and press Submit. A file goes in one of three ways: (1) a multipart/form-data POST with the part `documents` (and the other answers as JSON in the part `answers`); (2) in JSON as {"name": "resume.pdf", "type": "application/pdf", "base64": "..."}, only for a small file you already hold, never by reading a file into your context; (3) in JSON as {"url": "https://..."}, a public https link the form downloads. At most 10 MB a file, 50 MB in all; the bytes are checked, not the name. ## Submit over HTTP `POST https://ui.usecube.io/f/client-intake` with `Content-Type: application/json`: ```json { "answers": { "contact_name": "Jane Doe", "email": "jane@example.com", "phone": "+14155550123", "contact_method": [ "Email" ], "company_address": { "street": "1 Main St", "city": "Seattle", "region": "WA", "postal_code": "98101", "country": "US" }, "company_website": "https://example.com", "project_start": "2026-11-02" }, "agent": { "name": "Claude", "model": "claude-sonnet-4-5" }, "on_behalf_of": "Jane Doe" } ``` `answers` is required. `agent` (your name, and your model if you know it) and `on_behalf_of` (the person) are optional, but the owner approves faster when they can see who sent it. ```sh curl -X POST https://ui.usecube.io/f/client-intake \ -H 'Content-Type: application/json' \ -d '{"answers":{"contact_name":"Jane Doe","email":"jane@example.com","phone":"+14155550123","contact_method":["Email"],"company_address":{"street":"1 Main St","city":"Seattle","region":"WA","postal_code":"98101","country":"US"},"company_website":"https://example.com","project_start":"2026-11-02"},"agent":{"name":"Claude","model":"claude-sonnet-4-5"},"on_behalf_of":"Jane Doe"}' ``` With a file, send multipart/form-data instead: the answers as JSON in the part `answers`, each file as a part named after its question: ```sh curl -X POST https://ui.usecube.io/f/client-intake \ -F 'answers={"contact_name":"Jane Doe","email":"jane@example.com","phone":"+14155550123","contact_method":["Email"],"company_address":{"street":"1 Main St","city":"Seattle","region":"WA","postal_code":"98101","country":"US"},"company_website":"https://example.com","project_start":"2026-11-02"}' \ -F 'documents=@documents.pdf' \ -F 'agent={"name": "Claude"}' -F 'on_behalf_of=Jane Doe' ``` A success is `201 Created`: ```json { "id": "s_...", "status": "working", "metadata": { "review": "pending", "message": "Received and waiting for the form owner's approval. It counts once approved." }, "receipt_url": "https://ui.usecube.io/s/..." } ``` `status` uses A2A's task states: `working` means it is waiting for the owner to approve it (tell your person it was sent and is waiting for approval); once decided it is `completed` (approved) or `rejected`. `receipt_url` is your person's own view of what was sent; give it to them. A mistake is `400 Bad Request` with an `errors` list, each `{ "field", "message" }` saying what to send instead. Fix those answers and send again. `429 Too Many Requests` means wait the `Retry-After` seconds. ## Other formats - [JSON Schema of the answers](https://ui.usecube.io/f/client-intake.json) - [This page as Markdown](https://ui.usecube.io/f/client-intake.md) - MCP (Streamable HTTP): `https://ui.usecube.io/mcp`, tools `list_forms`, `read_form`, `submit_form`, `prefill_link`, `draft_form`. - In a browser with WebMCP, the page's form is the tool `submit_client_intake`. --- # How are we doing? > Tell a product team how likely you are to recommend them and why (a demo feedback form). Four questions, two minutes. Thank you. This is a form you can fill in for a person without opening a browser. Its fields are `recommend`, `reason`, `improve`, `email` (`recommend`, `reason` required). Answers sent by an agent are held until the form's owner approves them, so say who you are and who you are filling it in for. **Only send what your person told you. Ask them for any required answer you do not have, and never tick a consent box or answer a demographic question for them unless they told you to.** ## How to fill it 1. **If you can send HTTP requests:** `POST https://ui.usecube.io/f/feedback` with a JSON body `{"answers": {...}, "agent": {"name": "...", "model": "..."}, "on_behalf_of": "..."}`. Details below. 2. **If you have the Cube MCP connector** (`https://ui.usecube.io/mcp`): call `submit_form` with `id: "feedback"`, `answers` and `on_behalf_of`; or `prefill_link` with `id` and `answers` to get the link for your person (a file cannot ride in it; they attach it on the page). 3. **If you can't do either (a chatbot):** give your person this link with the answers filled in, each one URL-encoded (lists and objects as JSON). They check it and press Submit themselves: ``` https://ui.usecube.io/f/feedback#recommend=...&reason=...&improve=...&email=... ``` For example: https://ui.usecube.io/f/feedback#recommend=9&reason=...&improve=...&email=jane%40example.com ## Fields - **recommend** (integer 0 to 10, 0 = "Not at all likely", 10 = "Extremely likely", required): How likely the person is to recommend the product to a friend. - **reason** (string, up to 2,000 characters, required): Why they gave that score, in their own words. - **improve** (string, up to 2,000 characters): The one thing they would change. - **email** (string, email): Where the team can follow up, if the person wants that. ## Submit over HTTP `POST https://ui.usecube.io/f/feedback` with `Content-Type: application/json`: ```json { "answers": { "recommend": 9, "reason": "...", "improve": "...", "email": "jane@example.com" }, "agent": { "name": "Claude", "model": "claude-sonnet-4-5" }, "on_behalf_of": "Jane Doe" } ``` `answers` is required. `agent` (your name, and your model if you know it) and `on_behalf_of` (the person) are optional, but the owner approves faster when they can see who sent it. ```sh curl -X POST https://ui.usecube.io/f/feedback \ -H 'Content-Type: application/json' \ -d '{"answers":{"recommend":9,"reason":"...","improve":"...","email":"jane@example.com"},"agent":{"name":"Claude","model":"claude-sonnet-4-5"},"on_behalf_of":"Jane Doe"}' ``` A success is `201 Created`: ```json { "id": "s_...", "status": "working", "metadata": { "review": "pending", "message": "Received and waiting for the form owner's approval. It counts once approved." }, "receipt_url": "https://ui.usecube.io/s/..." } ``` `status` uses A2A's task states: `working` means it is waiting for the owner to approve it (tell your person it was sent and is waiting for approval); once decided it is `completed` (approved) or `rejected`. `receipt_url` is your person's own view of what was sent; give it to them. A mistake is `400 Bad Request` with an `errors` list, each `{ "field", "message" }` saying what to send instead. Fix those answers and send again. `429 Too Many Requests` means wait the `Retry-After` seconds. ## Other formats - [JSON Schema of the answers](https://ui.usecube.io/f/feedback.json) - [This page as Markdown](https://ui.usecube.io/f/feedback.md) - MCP (Streamable HTTP): `https://ui.usecube.io/mcp`, tools `list_forms`, `read_form`, `submit_form`, `prefill_link`, `draft_form`. - In a browser with WebMCP, the page's form is the tool `submit_feedback`. --- # Talk to sales > Ask a company's sales team to get in touch (a demo contact-sales form). Tell us what you need. We reply within one business day. This is a form you can fill in for a person without opening a browser. Its fields are `first_name`, `last_name`, `work_email`, `company`, `company_size`, `phone`, `needs` (`first_name`, `last_name`, `work_email`, `company`, `company_size`, `needs` required). Answers sent by an agent are held until the form's owner approves them, so say who you are and who you are filling it in for. **Only send what your person told you. Ask them for any required answer you do not have, and never tick a consent box or answer a demographic question for them unless they told you to.** ## How to fill it 1. **If you can send HTTP requests:** `POST https://ui.usecube.io/f/talk-to-sales` with a JSON body `{"answers": {...}, "agent": {"name": "...", "model": "..."}, "on_behalf_of": "..."}`. Details below. 2. **If you have the Cube MCP connector** (`https://ui.usecube.io/mcp`): call `submit_form` with `id: "talk-to-sales"`, `answers` and `on_behalf_of`; or `prefill_link` with `id` and `answers` to get the link for your person (a file cannot ride in it; they attach it on the page). 3. **If you can't do either (a chatbot):** give your person this link with the answers filled in, each one URL-encoded (lists and objects as JSON). They check it and press Submit themselves: ``` https://ui.usecube.io/f/talk-to-sales#first_name=...&last_name=...&work_email=...&company=...&company_size=...&phone=...&needs=... ``` For example: https://ui.usecube.io/f/talk-to-sales#first_name=Jane&last_name=Doe&work_email=jane%40example.com&company=Acme&company_size=1%E2%80%9310&needs=... ## Fields - **first_name** (string, up to 200 characters, required): The person's first name. - **last_name** (string, up to 200 characters, required): The person's last name. - **work_email** (string, email, required): The person's work email address. - **company** (string, up to 200 characters, required): The company they work for. - **company_size** (string, one of "1–10", "11–50", "51–200", "201+", required): How many people work at the company. - **phone** (string, E.164 phone number like +14155550123): A number sales can call. - **needs** (string, up to 3,000 characters, required): What they want from the product, in their own words. ## Submit over HTTP `POST https://ui.usecube.io/f/talk-to-sales` with `Content-Type: application/json`: ```json { "answers": { "first_name": "Jane", "last_name": "Doe", "work_email": "jane@example.com", "company": "Acme", "company_size": "1–10", "needs": "..." }, "agent": { "name": "Claude", "model": "claude-sonnet-4-5" }, "on_behalf_of": "Jane Doe" } ``` `answers` is required. `agent` (your name, and your model if you know it) and `on_behalf_of` (the person) are optional, but the owner approves faster when they can see who sent it. ```sh curl -X POST https://ui.usecube.io/f/talk-to-sales \ -H 'Content-Type: application/json' \ -d '{"answers":{"first_name":"Jane","last_name":"Doe","work_email":"jane@example.com","company":"Acme","company_size":"1–10","needs":"..."},"agent":{"name":"Claude","model":"claude-sonnet-4-5"},"on_behalf_of":"Jane Doe"}' ``` A success is `201 Created`: ```json { "id": "s_...", "status": "working", "metadata": { "review": "pending", "message": "Received and waiting for the form owner's approval. It counts once approved." }, "receipt_url": "https://ui.usecube.io/s/..." } ``` `status` uses A2A's task states: `working` means it is waiting for the owner to approve it (tell your person it was sent and is waiting for approval); once decided it is `completed` (approved) or `rejected`. `receipt_url` is your person's own view of what was sent; give it to them. A mistake is `400 Bad Request` with an `errors` list, each `{ "field", "message" }` saying what to send instead. Fix those answers and send again. `429 Too Many Requests` means wait the `Retry-After` seconds. ## Other formats - [JSON Schema of the answers](https://ui.usecube.io/f/talk-to-sales.json) - [This page as Markdown](https://ui.usecube.io/f/talk-to-sales.md) - MCP (Streamable HTTP): `https://ui.usecube.io/mcp`, tools `list_forms`, `read_form`, `submit_form`, `prefill_link`, `draft_form`. - In a browser with WebMCP, the page's form is the tool `submit_talk_to_sales`. --- # Report a bug > Report a bug in a product: what happened, how to reproduce it, how bad it is (a demo bug report). What went wrong, and how we can make it happen again. This is a form you can fill in for a person without opening a browser. Its fields are `what_happened`, `steps`, `expected`, `severity`, `environment`, `searched` (`what_happened`, `steps`, `expected`, `severity`, `environment`, `searched` required). Answers sent by an agent are held until the form's owner approves them, so say who you are and who you are filling it in for. **Only send what your person told you. Ask them for any required answer you do not have, and never tick a consent box or answer a demographic question for them unless they told you to.** ## How to fill it 1. **If you can send HTTP requests:** `POST https://ui.usecube.io/f/bug-report` with a JSON body `{"answers": {...}, "agent": {"name": "...", "model": "..."}, "on_behalf_of": "..."}`. Details below. 2. **If you have the Cube MCP connector** (`https://ui.usecube.io/mcp`): call `submit_form` with `id: "bug-report"`, `answers` and `on_behalf_of`; or `prefill_link` with `id` and `answers` to get the link for your person (a file cannot ride in it; they attach it on the page). 3. **If you can't do either (a chatbot):** give your person this link with the answers filled in, each one URL-encoded (lists and objects as JSON). They check it and press Submit themselves: ``` https://ui.usecube.io/f/bug-report#what_happened=...&steps=...&expected=...&severity=...&environment=... ``` For example: https://ui.usecube.io/f/bug-report#what_happened=...&steps=...&expected=...&severity=Low&environment=... ## Fields - **what_happened** (string, up to 5,000 characters, required): What went wrong, as the person saw it. - **steps** (string, up to 5,000 characters, required): The steps that make the bug happen, one per line. - **expected** (string, up to 3,000 characters, required): What should have happened instead. - **severity** (string, one of "Low", "Medium", "High", "Critical", required): How bad the bug is for the person. - **environment** (string, up to 200 characters, required): Where it happened: app version, browser or OS. - **searched** (boolean, must be true, required): The person checked that this bug is not already reported. Set it only if they did. Must be confirmed by the person; when submitting directly over HTTP or MCP, set it only if your person explicitly agreed. ## Submit over HTTP `POST https://ui.usecube.io/f/bug-report` with `Content-Type: application/json`: ```json { "answers": { "what_happened": "...", "steps": "...", "expected": "...", "severity": "Low", "environment": "..." }, "agent": { "name": "Claude", "model": "claude-sonnet-4-5" }, "on_behalf_of": "Jane Doe" } ``` `answers` is required. `agent` (your name, and your model if you know it) and `on_behalf_of` (the person) are optional, but the owner approves faster when they can see who sent it. ```sh curl -X POST https://ui.usecube.io/f/bug-report \ -H 'Content-Type: application/json' \ -d '{"answers":{"what_happened":"...","steps":"...","expected":"...","severity":"Low","environment":"..."},"agent":{"name":"Claude","model":"claude-sonnet-4-5"},"on_behalf_of":"Jane Doe"}' ``` A success is `201 Created`: ```json { "id": "s_...", "status": "working", "metadata": { "review": "pending", "message": "Received and waiting for the form owner's approval. It counts once approved." }, "receipt_url": "https://ui.usecube.io/s/..." } ``` `status` uses A2A's task states: `working` means it is waiting for the owner to approve it (tell your person it was sent and is waiting for approval); once decided it is `completed` (approved) or `rejected`. `receipt_url` is your person's own view of what was sent; give it to them. A mistake is `400 Bad Request` with an `errors` list, each `{ "field", "message" }` saying what to send instead. Fix those answers and send again. `429 Too Many Requests` means wait the `Retry-After` seconds. ## Other formats - [JSON Schema of the answers](https://ui.usecube.io/f/bug-report.json) - [This page as Markdown](https://ui.usecube.io/f/bug-report.md) - MCP (Streamable HTTP): `https://ui.usecube.io/mcp`, tools `list_forms`, `read_form`, `submit_form`, `prefill_link`, `draft_form`. - In a browser with WebMCP, the page's form is the tool `submit_bug_report`.