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 fullhttps://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.
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,PUTandPATCH. 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
Authorizationheader, 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-agentandsec-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.
-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:
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.
Test the request
Send test request sends the request once, for real, with the connection you picked.- A
GETgoes 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.
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, a4xx 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.
- 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 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:
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.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
GETalone, 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.
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 withmanage_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,PATCHandDELETE. - Body: JSON only (an object or a list), for
POST,PUTandPATCH, 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-Lengthand 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.
Related
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.
