curl --request POST \
--url https://app.goosybear.ai/api/v1/tools/desk.build_card \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "<string>",
"brief": "<string>",
"data_table_ids": [
"3c90c3cc-0d44-4b50-8888-8dd25736052a"
],
"confirmation_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"workspace": "<string>"
}
'import requests
url = "https://app.goosybear.ai/api/v1/tools/desk.build_card"
payload = {
"title": "<string>",
"brief": "<string>",
"data_table_ids": ["3c90c3cc-0d44-4b50-8888-8dd25736052a"],
"confirmation_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"workspace": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
title: '<string>',
brief: '<string>',
data_table_ids: ['3c90c3cc-0d44-4b50-8888-8dd25736052a'],
confirmation_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
workspace: '<string>'
})
};
fetch('https://app.goosybear.ai/api/v1/tools/desk.build_card', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.goosybear.ai/api/v1/tools/desk.build_card",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'title' => '<string>',
'brief' => '<string>',
'data_table_ids' => [
'3c90c3cc-0d44-4b50-8888-8dd25736052a'
],
'confirmation_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'workspace' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.goosybear.ai/api/v1/tools/desk.build_card"
payload := strings.NewReader("{\n \"title\": \"<string>\",\n \"brief\": \"<string>\",\n \"data_table_ids\": [\n \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n ],\n \"confirmation_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"workspace\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.goosybear.ai/api/v1/tools/desk.build_card")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"title\": \"<string>\",\n \"brief\": \"<string>\",\n \"data_table_ids\": [\n \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n ],\n \"confirmation_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"workspace\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.goosybear.ai/api/v1/tools/desk.build_card")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"title\": \"<string>\",\n \"brief\": \"<string>\",\n \"data_table_ids\": [\n \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n ],\n \"confirmation_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"workspace\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"state": "confirmation_required",
"title": "<string>",
"workspace": "<string>",
"working_in": {
"label": "<string>",
"note": "<string>",
"workspace": "<string>",
"source": "call-override"
},
"confirmation_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"expires_in_seconds": 123,
"request_summary": "<string>",
"card_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"revision": "<string>",
"entry": "<string>",
"file_count": 1,
"bytes": 1,
"reads": [
"<string>"
],
"door_url": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
}
}Call desk.build_card
Write a small custom card for this workspace — a little self-contained page, coded from the brief you give it, rendered in a sandbox and readable at its own address. Use it when what is wanted is not one of the ordinary elements: a bespoke view, a calculator, a countdown, a table shaped a particular way. Carry the operator’s own sentence into brief; it is the whole instruction the card is written from. If the card needs live data, pass every Data table it may read in data_table_ids — a table you do not pass is a table the card can never see. This runs a PAID authoring session, so it takes two calls: the first describes what would be built, charges nothing and returns a confirmation id, and the second — carrying that id back — is what actually builds it. It returns the card and does NOT put it on any board: placing an element is a desk action, and there is no verb here that can do it.
Every result names the workspace it ran in. Say which workspace the answer is about. When the account has more than one workspace and none is selected, this tool refuses with workspace_ambiguous and lists the choices — offer them, never pick one.
curl --request POST \
--url https://app.goosybear.ai/api/v1/tools/desk.build_card \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "<string>",
"brief": "<string>",
"data_table_ids": [
"3c90c3cc-0d44-4b50-8888-8dd25736052a"
],
"confirmation_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"workspace": "<string>"
}
'import requests
url = "https://app.goosybear.ai/api/v1/tools/desk.build_card"
payload = {
"title": "<string>",
"brief": "<string>",
"data_table_ids": ["3c90c3cc-0d44-4b50-8888-8dd25736052a"],
"confirmation_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"workspace": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
title: '<string>',
brief: '<string>',
data_table_ids: ['3c90c3cc-0d44-4b50-8888-8dd25736052a'],
confirmation_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
workspace: '<string>'
})
};
fetch('https://app.goosybear.ai/api/v1/tools/desk.build_card', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.goosybear.ai/api/v1/tools/desk.build_card",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'title' => '<string>',
'brief' => '<string>',
'data_table_ids' => [
'3c90c3cc-0d44-4b50-8888-8dd25736052a'
],
'confirmation_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'workspace' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.goosybear.ai/api/v1/tools/desk.build_card"
payload := strings.NewReader("{\n \"title\": \"<string>\",\n \"brief\": \"<string>\",\n \"data_table_ids\": [\n \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n ],\n \"confirmation_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"workspace\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.goosybear.ai/api/v1/tools/desk.build_card")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"title\": \"<string>\",\n \"brief\": \"<string>\",\n \"data_table_ids\": [\n \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n ],\n \"confirmation_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"workspace\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.goosybear.ai/api/v1/tools/desk.build_card")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"title\": \"<string>\",\n \"brief\": \"<string>\",\n \"data_table_ids\": [\n \"3c90c3cc-0d44-4b50-8888-8dd25736052a\"\n ],\n \"confirmation_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"workspace\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"ok": true,
"state": "confirmation_required",
"title": "<string>",
"workspace": "<string>",
"working_in": {
"label": "<string>",
"note": "<string>",
"workspace": "<string>",
"source": "call-override"
},
"confirmation_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"expires_in_seconds": 123,
"request_summary": "<string>",
"card_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"revision": "<string>",
"entry": "<string>",
"file_count": 1,
"bytes": 1,
"reads": [
"<string>"
],
"door_url": "<string>"
}{
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
}
}{
"error": {
"code": "<string>",
"message": "<string>",
"retry_after_seconds": 123
}
}Authorizations
An API key minted at Settings › API & MCP. Send it as Authorization: Bearer <key>. A key carries its holder's own permissions, resolved on every call — revoking a membership closes the key's reach immediately. Keep it in an environment variable (GOOSY_API_KEY), never in a committed file.
Body
What to call the card — its label on the desk.
1 - 120What the card should show, in the member's own words.
1 - 4000Every Data table this card may read, and the only ones. Leave it out for a card that reads nothing.
8Leave this out on the first call: the answer describes what would be built, charges nothing and hands you an id. Send that id back, unchanged, to actually build it.
Which workspace to run in — its slug. Omit to use your default. With more than one reachable workspace and no default, the call is refused and the choices are listed.
1Response
The call was admitted and dispatched. ok says whether the tool succeeded — a refusal the tool itself produced is still a 200, exactly as it is a successful JSON-RPC result over MCP.
- Option 1
- Option 2
The tool ran and answered.
confirmation_required — nothing was written and nothing was charged; built — the card exists.
confirmation_required, built What the card is called.
The slug of the workspace this call ran in.
Which workspace this call ran in, and how that was decided. Present on every workspace-scoped result.
Show child attributes
Show child attributes
Present only while confirmation is required.
How long that confirmation stays usable.
Present only while confirmation is required — what will be built, in one sentence, for a person to approve.
Present once built — the card's durable identity.
Present once built — the build this card is now on.
Bundle-relative path of the document the frame loads.
x >= 0x >= 0What this build declared it may read, and the only ones.
Present once built — a short-lived address that renders this card. Null when one could not be signed for this environment; the card is still built. Treat it as a secret: it is a bearer capability and it expires.