Output format
Every tool call returns three things:
| Part | For | Content |
|---|---|---|
content[0].text |
The model | A Markdown summary: what was found or done, and what to do next. |
structuredContent |
The client and the card | The data, valid against the tool’s outputSchema, with a kind that says what it is. |
The card (_meta.ui.resourceUri) |
The user, in the Claude app | The VibeSell card, which renders structuredContent by its kind. |
isError is false for a result and true for a refusal; a refusal carries only text. See
Errors & refusals.
An example
Section titled “An example”{ "content": [ { "type": "text", "text": "**Question for the user:** Choose your sending addresses\n_This decides what gets set up for you. Nothing is set up, reserved or billed by this answer._\n\n- `already_warmed` — Start sooner (…)\n- `own_names` — Send under my brand (…)\n- or an answer in their own words\n\nPut this to the user and wait for THEIR answer — never pick for them. Then call choose_sending_source again with the same arguments and answers: [{\"card\":\"ecdbf5a52b3ecea6\",\"answer\":\"<their answer>\"}]…" } ], "structuredContent": { "kind": "choice", "tool": "choose_sending_source", "arguments": {}, "card": "ecdbf5a52b3ecea6", "question": "Choose your sending addresses", "reason": "This decides what gets set up for you. Nothing is set up, reserved or billed by this answer.", "choices": [ { "id": "already_warmed", "label": "Start sooner", "description": "Addresses already prepared, without your brand's name. …" }, { "id": "own_names", "label": "Send under my brand", "description": "Addresses created for you, ready after about 2 business days of preparation, weekends not counted." } ], "allowFreeText": true, "answers": [] }, "isError": false}Result kinds
Section titled “Result kinds”Each tool’s result carries a kind. Clients can switch on it to render results their own way.
Questions and answers
Section titled “Questions and answers”Tools that ask the user something — choose_sending_source, propose_domains,
propose_warmed_domains, propose_email — return kind: "choice" with:
card— the id of this question;question,reasonandchoices(each with anid, alabeland maybe adescription);allowFreeText— whether the user may answer in their own words;arguments— the arguments to call the tool with again;answers— the answers given so far.
The assistant puts the question to the user, then calls the same tool with the same arguments and
answers set to the list it got back plus { "card": "<card id>", "answer": "<choice id or the user's words>" }.
An answer is only accepted for the very card it was given to: if the question changed in between,
the tool asks again. Once settled, the result switches to sending_choice or email_step.
Approval results
Section titled “Approval results”The six approval tools return kind: "approval" with a status — pending, done, not_done or
rejected — the card’s action, summary, server-written impact, irreversible flag and
expiresAt, plus decideHere and appUrl. See Approval cards.
Links in results
Section titled “Links in results”Results point the user back to VibeSell when something is decided or done there: appUrl (the
app), billing links from open_billing_portal, and the Stripe payment page from start_checkout.
Only the user opens them.