Skip to main content
Every app in the catalog comes with ready-made actions, but no list covers everything an app can do. A custom action fills the gap: you describe one request to the app’s API, and it runs on the connection you already have. You never type a password, API key or token. The connection signs the request, the same way it signs the app’s ready-made actions.

When to use it

  • The action you need is missing. You want to archive a deal, add a tag or read a report, and the app’s action list has nothing for it.
  • An action exists but lacks an option. The API takes a field or a filter the ready-made action doesn’t offer.
  • Your team makes the same call in many places. Save it once, and it sits beside the app’s own actions for everyone in the workspace.
  • Code or AI needs to reach further. A code step or an AI step can call any part of the app’s API, not only the actions you enabled.
A custom action reaches only apps you have connected, with that connection’s own sign-in. For a service you have no connection to, or one you sign in to with your own key, use the HTTP Request node.

How it works

You choose the method and the address inside the app’s API, and add the values the call needs. CogniAgent fills in your workflow values, checks the request, and sends it with your connection’s credentials. The request can only go to the app’s own API. Headers that carry credentials are refused, so nobody can swap in another account or send your connection’s access somewhere else.

Which apps support it

Most apps in the catalog do. When you open an app’s action list, the list ends with Build a custom action. Some apps can’t take one: apps that work through an MCP server and have no web API to call, apps whose API the connection can’t sign (a database, a file server or a cloud API that needs signed requests), and some built-in apps. For those, the row stays in the list, marked Not available, with the reason in plain words, so you never wonder where it went.
For a few apps, support depends on the account. If the app can’t take the request, your first test says so: “This app cannot take this request.”

Build a custom action

1

Open the builder

Add an Integration Action step and choose the app. Under Choose an action, click Build a custom action at the bottom of the list: “Call any HubSpot endpoint with this connection”.Searching works too. When no action matches your search, the list says so and offers Build a custom action. Your search words are then waiting in Help me build it, and they also fill in the name if you save the action.
2

Pick the connection

Choose the connection that signs the request. Every call runs as that account, so it needs the rights the call needs.
3

Set the method and path

Under Request, pick the method and type the path, for example /crm/v3/objects/deals. Or let Help me build it fill it in, or paste a cURL command from the app’s API docs.
4

Add parameters, headers and a body

Use the Params, Headers and Body tabs for what the call needs. Each value takes {{…}} workflow values.
5

Send a test

Under Test, click Send test request and check the answer before the workflow ever runs. See Test the request.

Method and path

The method says what the request does: The path is the part of the address after the app’s API address. Under the path, a chip shows where it goes:
  • Most apps show their API address, for example api.hubapi.com. Type a path that starts with /. A full https:// address works too, as long as it is on the app’s own domain.
  • Apps where each account has its own address (your company’s own Zendesk or Salesforce domain, for example) show “your Zendesk address”. Use a path, never a full address: the connection knows the account’s address.
  • A few apps have no base address. The path field then expects a full https:// address on the domain the builder names.
The second chip, “Signed by your HubSpot connection”, is a reminder that the credentials come from the connection.

Params, headers and body

  • Params are the query values added to the address, such as limit = 50. Click Add parameter for each one.
  • Headers are extra headers the API asks for, such as an API version header. The first row, Authorization, is locked: “Added by your HubSpot connection”. A header that carries credentials is refused with “The connection signs this request, so this header is not allowed”. That covers Authorization, Cookie, and any header named like a token, secret, password or API key.
  • Body is the data you send, written as JSON. It is available for POST, PUT and PATCH. Format tidies it up.

Import a cURL command

API docs often show each call as a cURL command, and your browser’s developer tools can copy any request as one (“Copy as cURL”). Click Import cURL beside Request, paste the command and click Import. Pasting a command straight into the path field works too. The request fills in: the method, the path (the app’s address is taken off, so only the path stays), the parameters, the headers and the JSON body. Imported from cURL says what changed, with Undo:
  • Credentials are left out. An Authorization header, a user and password (-u), cookies and API-key headers are dropped, because your connection signs the request. The panel lists what it left out.
  • Browser headers are left out, such as user-agent and sec-ch-ua.
  • Form fields become JSON fields. Custom requests send JSON, so check that the app accepts JSON.
  • A command for another address keeps only its path, and the panel names the address it called.
A command that uploads a file (-F, -T), reads its body from a file (-d @body.json), or sends a method other than GET, POST, PUT, PATCH or DELETE is not imported, and the panel says why.

Use workflow values

The path, the parameter and header values, and the body all take {{…}} values from earlier steps, the same way other step fields do (see Passing information between nodes). For example, a body that creates a HubSpot deal from a form submission:
A value that fills a whole field keeps its type. If amount is the number 1200, the app receives the number, not the text "1200". A test has no workflow run to take values from, so the test area lists every {{…}} value under Test values:
A test has no workflow run, so give each value here. Runs use the real ones.
When earlier runs left an example value, it is already filled in.

Help me build it

Describe the call in your own words, and an AI assistant drafts the request for you. It opens on its own when the request is empty. Otherwise, click Help me build it beside Request.
1

Say what the app should do

For example: “archive the deal whose id comes from the trigger”. While it works, the panel reads “Reading the HubSpot API”: it looks up the app’s own API documentation.
2

Check the draft

The assistant fills in the method, path, parameters, headers and body, and says in a sentence or two why. Its answer is marked Applied to the request, with Undo, and often an API docs link to the page it used.
3

Ask for changes

Reply in your own words, such as “also set the close date to today”. Each answer replaces the draft, and Undo brings back the one before.
If one of the app’s ready-made actions already does the job, the assistant says so and offers Use “Archive deal” instead, which switches the step to that action. The assistant never sends anything. Its drafts follow the same rules as yours (no credential headers, only the app’s own domain), and you still press Send test request yourself. It also suggests a name and a description, which fill in the save form. Each answer uses AI, so it uses credits like the platform’s other AI helpers.

Test the request

Send test request sends the request once, for real, with the connection you picked.
  • A GET goes out at once.
  • Any other method asks first: “This sends a real POST to HubSpot as Sales team. It can change data there.” Click Send it or Cancel.
A test is a real call. A test POST creates a real record, and a test DELETE deletes one. Test against data you can afford to change.
The answer shows its status (200, 404), how long it took, its size, and the body. Headers opens the response headers that are kept (see Limits). When the app answers with a file, such as a PDF or an image, the test shows the file’s name, type and size, and Open opens it. A workflow run keeps the file too: see file in What the step returns. When the request could not be sent, the panel says why, with a hint where one helps: After a failed test, Fix with AI sends the status and the app’s answer to Help me build it, which proposes a corrected request. You review it and send the next test. To send a test you need a confirmed email address and permission to use the connection.

When the app answers with an error

By default, a 4xx or 5xx answer fails the step, like any Integration Action. The failed step shows the status and the start of the app’s answer, for example HubSpot answered 404: {"message":"Object not found"}, and the rest of the workflow handles it as any failure. To handle errors yourself, open More options and turn on Continue when HubSpot answers with an error:
Off: a 4xx or 5xx answer fails this step. On: the step passes the status and body on to the next steps.
Then branch on the answer with a Condition, for example on {{external_api_1.ok}} or {{external_api_1.status}}.

What the step returns

any
The app’s answer: JSON when the app sends JSON, otherwise text. Read fields with {{external_api_1.data.id}}. Empty when the app answers with a file.
object
A file the app answered with (a PDF, an image, an export), saved with the run: its name, size and mimeType. Pass {{external_api_1.file}} to a later step that takes a file, such as an HTTP Request upload.
number
The HTTP status the app answered, such as 200 or 404.
boolean
true when the status is in the 200s.
object
The useful response headers, such as the content type, paging links and rate-limit headers. The others are dropped.
object
The method and path that were sent, without the query values.

Save it as a reusable action

A request you build in one step can become an action anyone in the workspace can pick, right beside the app’s ready-made actions. Agents, AI steps, coworkers and code can use it too.
1

Open the save form

At the bottom of the builder, open Save as a reusable action: “Everyone in this workspace can pick it; agents and code can use it too.”
2

Name it

Give it a Name (up to 80 characters), such as “Archive deal”. Fill in When to use it with what it does, in one sentence. Agents read this to decide when to call it.
3

Check the inputs

Each workflow value in the request becomes an input, found for you and named after the value: {{trigger.deal_id}} becomes deal_id. Pick each input’s type (Text, Number, Whole number, True or false, List or Object) and whether it is Required. Input names use lowercase letters, digits and _.
4

Mark a read-only POST

Only for a POST that changes nothing, such as a search, tick It only reads data (for example a search). Coworkers then use it without asking first. Leave it off when the call changes anything.
5

Save

Click Save action. The step now runs the saved action and passes each input the same workflow value as before, so it keeps working unchanged.
Once saved, the action shows up:
  • In workflow steps, under Your custom actions at the top of the app’s action list, with its method and a Custom tag.
  • For AI steps, agents and coworkers, in the app’s action list, marked Custom. Switch it on like any action.
  • In code, as a function named after the action, once you enable it on the code step. See Use it from code.
A saved action belongs to one app, and everyone in the workspace can see, use and change it.

A step that runs a saved action

The step shows the action’s name, its Custom tag, its method and path, and Edit. Below that come the connection, one field per input (each takes {{…}}), Continue when HubSpot answers with an error, and a test.

Edit or delete a saved action

Open any step that uses the action and click Edit. Before you change anything, the panel says what uses it:
Used in 3 workflows, 1 agent and 2 coworkers. A change reaches all of them on their next run.
You can change the name, When to use it, the request, the inputs (Add input, change a type, remove one) and, for a POST, whether it only reads data. In the saved request, inputs are written as {{args.<input name>}}, for example /crm/v3/objects/deals/{{args.deal_id}}. Click Save changes, and every step, agent and coworker that uses it runs the new version from its next run. Delete action asks you to confirm and names what uses it. After deleting:
  • Steps that used it can’t run, and show ““Archive deal” was deleted, so this step cannot run. Choose another action.”
  • Agents, AI steps, coworkers and code no longer get it.

Use it from code

When you bind an app to an Execute Code step, and the app supports custom actions, the code gets one more function, with nothing to enable:
It is listed under Integration functions with the app’s other functions, and the code step’s integration panel shows the exact call under Any endpoint from code. It returns a response with: A 4xx or 5xx answer is returned, not raised, so your code can read the app’s reason. IntegrationError is raised only when nothing could be sent: the request was refused, the app does not support it, the connection is missing, the workspace hit its rate limit, or the app did not answer in time.
If the app already has a ready-made action whose function is called request, it keeps that name, and the custom call is integrations.<app>.api_request(...) instead. The editor shows the name to use.
A saved custom action works like any other function once you enable it on the code step: integrations.hubspot.archive_deal(deal_id="1882"). It returns the app’s data, and a 4xx or 5xx answer raises IntegrationError with code http_<status>. Custom calls count toward the step’s 200 calls per run, like every app call.

Use it from AI steps and agents

An LLM step, a Human Step or a Chat & Voice Agent actor can call the app’s API too, when you allow it. In the app’s integration panel, above the action list, Custom API calls has three settings: With Read only or Read and write, the AI gets one more ability for that app: a request to any part of its API, signed by the same connection. It is told to use it only when no action fits, and never to add credentials.
  • Read only is enforced. The AI is offered GET alone, and any other method is refused before it is sent.
  • The AI reads the app’s answer, errors included, so it can see why a request failed and fix it.
  • Long answers are cut to the first 100,000 characters, with a note telling the AI to ask for less.
  • With Custom API calls on, you can save the app with no action checked.
Read and write lets the AI change data in the app without asking. Start with Read only, and enable write access only where the step needs it.
Coworkers have the same setting, and they ask you before any call that changes data. See Connecting your apps.

Build them with an AI assistant

Over MCP, an AI assistant such as Claude or Cursor can find out whether an app takes custom requests, add a custom request step, and create, update or delete saved custom actions with manage_custom_action.

Cost

  • A workflow step that runs a custom action costs the same as an Integration Action step with a ready-made action.
  • A custom call from code, an AI step, an agent or a coworker costs the same as a call to a ready-made action.
  • Help me build it uses AI, and each answer uses credits.

Limits and rules

  • Methods: GET, POST, PUT, PATCH and DELETE.
  • Body: JSON only (an object or a list), for POST, PUT and PATCH, up to 5 MB.
  • Answer: up to 25 MB. JSON comes back as data, text as text, and a file is saved with the run.
  • Time: the app has 30 seconds to answer.
  • Rate: up to 60 custom requests a minute per workspace, shared by workflow steps, tests, code, AI steps, agents and coworkers. Past that, the request is refused with “Too many custom requests from this workspace. Try again in N s.”
  • Address: a path on the app’s API, or a full https:// address on the app’s own domains. Nothing else.
  • Headers: never Authorization, Cookie, API keys or other credentials, and none of the headers the browser or the network sets (Host, Origin, Content-Length and the like). Your connection signs every request.
  • Response headers: only the useful ones are kept: content type, length and disposition, Link, Location, ETag, Last-Modified, Retry-After, request ids and rate-limit headers. Cookies and the rest are dropped.
  • Size: up to 50 parameters and 50 headers per request.
  • Saved actions: a name of up to 80 characters, a description of up to 500, and up to 30 inputs.

Integration Action

Run one of an app’s ready-made actions.

Execute Code integrations

Call your apps from Python, in loops and batches.

LLM Integrations

Let AI decide which app actions to call.

HTTP Request

Call any web service with your own credentials.

Connecting your apps

Custom API calls for coworkers, and how they ask first.

Connect MCP

Build steps and saved actions from your AI assistant.