Skip to content
Cook
Documentation Browse

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 callOn CookNotes
GET /meSupportedThe key’s person (or the workspace’s name for a workspace key).
GET /formsSupportedpage, page_size (up to 200), search, workspace_id, is_public, sort_by, order_by.
POST /formsPartialCreates 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}SupportedReads back what you wrote, including refs, choice ids, descriptions and welcome screens.
PUT /forms/{form_id}PartialReplaces the form. Field ids and refs are kept. Same field rules as POST.
PATCH /forms/{form_id}SupportedJSON 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}PartialDeletes 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}/responsesSupportedEvery 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/searchSupportedfilter_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}/responsesPartialincluded_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/filesSupportedAnswers 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 supported404: Cook forms collect no files.
GET /forms/{form_id}/webhooksSupported
GET /forms/{form_id}/webhooks/{tag}Supported
PUT /forms/{form_id}/webhooks/{tag}SupportedCreate or update. The URL must be public https.
DELETE /forms/{form_id}/webhooks/{tag}Supported
GET /forms/{form_id}/webhooks/{tag}/deliveriesSupported (Cook extra)The hook’s delivery log, newest first. Typeform shows this only in its admin.
GET /workspaces, GET /accounts/{account_id}/workspacesSupported
GET /workspaces/{workspace_id}SupportedMembers are your Cook workspace’s team, when the key may read the team.
POST /workspaces, POST /accounts/{account_id}/workspacesSupportedMakes a folder for forms (see Gotchas).
PATCH /workspaces/{workspace_id}PartialRenames a workspace made here. Changing /members answers 403: invite and remove people in Cook.
DELETE /workspaces/{workspace_id}PartialOnly an empty workspace you made here; the default one can’t be deleted (403).
Images API (/images)Not supportedCook keeps no Typeform image library. Pictures on choices are taken as https links.
Themes API (/themes)Not supportedCook’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 supportedCook’s hosted form has its own wording.
Translations (/forms/{form_id}/translations)Not supportedCook forms have one language.
Insights (/insights/{form_id}/summary)Not supportedCook 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 typeCook stores it as
short_texttext (or a ZIP code field made in Cook)
websitetext
long_textlong text
emailemail
phone_numberphone
numbernumber
dropdowndropdown
multiple_choicesingle choice
picture_choicesingle choice with a picture link on each choice
yes_nosingle choice with the answers Yes and No
legalcheckbox
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); required works
  • logic that does anything but jump to an ending: Cook asks every question, and it doesn’t calculate variables, so calculated.score is always 0. Jump conditions may use is, equal, is_not, not_equal, contains, greater_than, lower_than, always and and.
  • 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 secret and each delivery carries Typeform-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 404 or 410 from your server switches the hook off. A 429, 408, 503 or 423 is 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. PUT without event_types listens for form_response. A PUT that sends only { "enabled": false } (the SDK’s toggle) keeps the URL and secret. A new hook is off until you send enabled: 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 https address. An http URL answers 400 with code webhook_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 204 should 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 /workspaces makes extra workspaces that only group forms for the API; Cook itself shows all forms together. Members are your Cook team (owners and admins read as owner, everyone else as member).

  • Response ids. token, response_id and landing_id are the same value, a Typeform-style id Cook gives each submission and keeps. Use it for included_response_ids, after, before and deletes.

  • No partial or started responses. response_type=partial, response_type=started and completed=false return an empty list.

  • Times. landed_at equals submitted_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 since and until.

  • 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_public is whether the form is live: when it’s false, the form’s link stops taking submissions.

  • The form’s link. _links.display is the form’s Cook link (…/f/<token>), not a form.typeform.com address.

  • Limits. Cook’s own limits apply: 300 calls a minute per key, with bursts up to 600, then 429 with Retry-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.