Move from another tool
Typeform's API on Cook
Keep your Typeform scripts and SDK code. Point them at Cook's Typeform address with a Cook key, and your forms, responses and webhooks answer the way Typeform's do.
Cook answers Typeform’s Create, Responses and Webhooks APIs. A script that
creates forms, reads responses or listens for form_response webhooks keeps
working when you point it at Cook. Your forms then live in Cook, and the
people who fill them in become leads in your Cook workspace.
An AI agent on Cook’s MCP finds these forms with form_list and their
responses as leads, with no import; see
Using it from an AI agent.
The switch: two lines
Change the address and the key. Nothing else changes.
Base URL: https://api.trycook.ai/compat/typeform
Authorization: Bearer ck_live_…
Make the key in Cook under Settings → API keys. The key decides the workspace, and its permissions decide what the script may do (see the compat overview).
With Typeform’s official JavaScript SDK (@typeform/api-client), it is one
line:
const typeform = createClient({ token: "ck_live_…", apiBaseUrl: "https://api.trycook.ai/compat/typeform" });
Everything after the address is as Typeform documents it: the paths, query
parameters, request and response bodies, page sizes and the error body
({ "code", "description", "details" }).
What Cook answers
Supported means it behaves as Typeform documents it. Partial says what differs. Not supported answers Typeform’s error and says why.
| Typeform call | On Cook | Notes |
|---|---|---|
GET /me | Supported | The key’s person (or the workspace’s name for a workspace key). |
GET /forms | Supported | page, page_size (up to 200), search, workspace_id, is_public, sort_by, order_by. |
POST /forms | Partial | Creates a Cook form. Field types Cook can’t collect are refused (see Field types). The new form gets a new Typeform-style id; field ids you send are kept. |
GET /forms/{form_id} | Supported | Reads back what you wrote, including refs, choice ids, descriptions and welcome screens. |
PUT /forms/{form_id} | Partial | Replaces the form. Field ids and refs are kept. Same field rules as POST. |
PATCH /forms/{form_id} | Supported | JSON Patch on /title, /theme, /workspace and /settings/is_public, /settings/meta, /settings/facebook_pixel, /settings/google_analytics, /settings/google_tag_manager, /settings/enrichment_in_renderer, /settings/email_consent_notifications_config. |
DELETE /forms/{form_id} | Partial | Deletes the form and its responses (204) for an owner’s or admin’s key, or a workspace key. Any other key gets 202 and an approval card (see Gotchas). |
GET /forms/{form_id}/responses | Supported | Every Typeform filter: page_size (up to 1000), since, until, after, before, included_response_ids, excluded_response_ids, response_type, completed, sort, query, fields, answered_fields. |
POST /forms/{form_id}/responses/search | Supported | filter_group with equals, any_of, interval, contains on response_id, timestamp, tags, answers.{field_id}, metadata.{key}, hidden.{key}; sort, size, query. |
DELETE /forms/{form_id}/responses | Partial | included_response_ids or included_tokens, up to 1000. Needs an owner’s or admin’s key, or a workspace key; other keys get 403. |
GET /forms/{form_id}/responses/files | Supported | Answers 204 (“no file upload fields in the form”), as Typeform does for such a form. Cook forms have no file upload field, so there are never files. |
GET /forms/{form_id}/responses/{response_id}/fields/{field_id}/files/{filename} | Not supported | 404: Cook forms collect no files. |
GET /forms/{form_id}/webhooks | Supported | |
GET /forms/{form_id}/webhooks/{tag} | Supported | |
PUT /forms/{form_id}/webhooks/{tag} | Supported | Create or update. The URL must be public https. |
DELETE /forms/{form_id}/webhooks/{tag} | Supported | |
GET /forms/{form_id}/webhooks/{tag}/deliveries | Supported (Cook extra) | The hook’s delivery log, newest first. Typeform shows this only in its admin. |
GET /workspaces, GET /accounts/{account_id}/workspaces | Supported | |
GET /workspaces/{workspace_id} | Supported | Members are your Cook workspace’s team, when the key may read the team. |
POST /workspaces, POST /accounts/{account_id}/workspaces | Supported | Makes a folder for forms (see Gotchas). |
PATCH /workspaces/{workspace_id} | Partial | Renames a workspace made here. Changing /members answers 403: invite and remove people in Cook. |
DELETE /workspaces/{workspace_id} | Partial | Only an empty workspace you made here; the default one can’t be deleted (403). |
Images API (/images) | Not supported | Cook keeps no Typeform image library. Pictures on choices are taken as https links. |
Themes API (/themes) | Not supported | Cook’s hosted form uses your workspace’s brand, not Typeform themes. A form’s theme.href is stored and read back, but there is no /themes route. |
Custom messages (/forms/{form_id}/messages) | Not supported | Cook’s hosted form has its own wording. |
Translations (/forms/{form_id}/translations) | Not supported | Cook forms have one language. |
Insights (/insights/{form_id}/summary) | Not supported | Cook shows views and submission counts on the form in Cook. |
Field types
These Typeform field types work in both directions: a script writes them and reads them back as the same type, ref and choices.
| Typeform type | Cook stores it as |
|---|---|
short_text | text (or a ZIP code field made in Cook) |
website | text |
long_text | long text |
email | |
phone_number | phone |
number | number |
dropdown | dropdown |
multiple_choice | single choice |
picture_choice | single choice with a picture link on each choice |
yes_no | single choice with the answers Yes and No |
legal | checkbox |
hidden fields (hidden: [...]) | hidden field |
Cook can’t collect these, so a form that has one is refused: calendly,
checkbox, contact_info, date, file_upload, google_calendar, group,
matrix, multi_format, nps, opinion_scale, payment, ranking,
rating, signature, statement, address, inline_group.
The answer is Typeform’s validation error, naming the field:
{
"code": "VALIDATION_ERROR",
"description": "The request is not valid",
"details": [
{
"code": "NOT_ALLOWED_VALUE",
"description": "Cook forms cannot collect a \"file_upload\" field (\"Upload your logo\")",
"field": "/fields/3/type",
"in": "body"
}
]
}
Also refused with the same kind of error, because Cook’s form would not do what the script asked:
allow_multiple_selection: true(a Cook choice question takes one answer)allow_other_choice: true(Cook has no “Other” answer)- the validations
max_length,min_value,max_value,min_selection,max_selection(Cook doesn’t enforce them);requiredworks - logic that does anything but jump to an ending: Cook asks every question,
and it doesn’t calculate variables, so
calculated.scoreis always0. Jump conditions may useis,equal,is_not,not_equal,contains,greater_than,lower_than,alwaysandand. - more than 30 fields (hidden fields count), a question longer than 120 characters, more than 25 choices or a choice longer than 120 characters, a form title longer than 80 characters
- a hidden field name that isn’t lower-case letters, digits and
_(up to 40)
Webhooks
A form_response webhook fires each time someone submits the form, however
they reached it: the form’s link, a page that shows the form, or a form filled
on your site. The body is Typeform’s: event_id, event_type, and
form_response with form_id, token, landed_at, submitted_at,
hidden, calculated, definition, answers and ending. The token and
the answers are the same ones the responses API returns for that response.
- Signing. Give the hook a
secretand each delivery carriesTypeform-Signature: sha256=<signature>, where the signature is the base64 HMAC-SHA256 of the raw body with your secret, as Typeform signs. Your existing check works unchanged. - Retries, on Typeform’s schedule. A
404or410from your server switches the hook off. A429,408,503or423is retried every 2 to 3 minutes for up to 10 hours. Any other failure is retried 5 times, after 5 minutes, 10 minutes, 20 minutes, 1 hour and 2 hours, then given up. Your server has 30 seconds to answer. - Creating and toggling.
PUTwithoutevent_typeslistens forform_response. APUTthat sends only{ "enabled": false }(the SDK’s toggle) keeps the URL and secret. A new hook is off until you sendenabled: true, as on Typeform. form_response_partial. You can subscribe to it and it reads back as subscribed, but it never fires: Cook keeps only submitted responses, so there are no partial ones to send.- The URL must be a public
httpsaddress. AnhttpURL answers400with codewebhook_url_https_required.
Gotchas
-
Deleting a form with a key that isn’t an owner’s or admin’s. Cook doesn’t delete it. It files an approval card in the workspace, the same card Cook’s AI files, and answers
202:{ "status": "pending_approval", "description": "Cook filed an approval card to delete this form and its responses; the form is deleted when a person in the workspace approves it.", "approval_id": "…" }The form stays until someone approves the card. A script that expects
204should use an owner’s or admin’s key, or a workspace key. -
Workspaces. Your Cook workspace is one Typeform account. Its default Typeform workspace is the Cook workspace itself: it can’t be renamed or deleted through the API.
POST /workspacesmakes extra workspaces that only group forms for the API; Cook itself shows all forms together. Members are your Cook team (owners and admins read asowner, everyone else asmember). -
Response ids.
token,response_idandlanding_idare the same value, a Typeform-style id Cook gives each submission and keeps. Use it forincluded_response_ids,after,beforeand deletes. -
No partial or started responses.
response_type=partial,response_type=startedandcompleted=falsereturn an empty list. -
Times.
landed_atequalssubmitted_at: Cook records when the form was sent, not when the page opened. New responses show up at once, without the half-hour delay Typeform warns about. -
Big forms. One read of the responses API looks at up to 4,000 of the form’s newest submissions in the time window you give. For more, page with
sinceanduntil. -
What Cook’s hosted form shows. Refs, choice ids, field descriptions, welcome screens, the theme and settings Cook has no use for are stored so the API reads back what you wrote, but the form people fill in on Cook doesn’t show them.
settings.is_publicis whether the form is live: when it’sfalse, the form’s link stops taking submissions. -
The form’s link.
_links.displayis the form’s Cook link (…/f/<token>), not aform.typeform.comaddress. -
Limits. Cook’s own limits apply: 300 calls a minute per key, with bursts up to 600, then
429withRetry-After.
What can’t move
- Links and embeds. Typeform links (
form.typeform.com/to/…) and Typeform embed codes on your site keep pointing at Typeform. Paste the Cook form’s link (_links.display) or show the form on a Cook page instead. - Past responses. Typeform’s API has no way to write responses, so a script can’t copy old ones in through this API.
- File uploads and payments. Cook forms collect neither, so forms that use them need another way to collect those.
- Partial responses, calculated scores and branching between questions. Cook keeps submitted responses only, doesn’t score, and asks every question; jumps can only go to an ending.
- Typeform’s look. Themes, custom messages and translations stay in Typeform. Your Cook form uses your workspace’s brand.
- Typeform’s own integrations. Its native Google Sheets, HubSpot and similar connections don’t come along. Webhooks you register here, and Cook’s CRM, take their place.
To bring a form over yourself, read it from Typeform (GET /forms/{form_id}
at api.typeform.com) and send the same body to Cook’s POST /forms. It is
accepted as long as its field types are ones Cook can collect.