Write or AI-generate JavaScript that runs as the final step of any automation, with access to the trigger data, the TrustPager API, outbound HTTP calls, and your own saved secrets. For technical and fulfillment users comfortable with basic JavaScript.
Custom Script lets you write your own JavaScript, or describe what you want in plain English and have AI write it for you, and run that code automatically whenever an automation fires. It is the most flexible action on the platform: if a trigger can hand you data, Custom Script can do almost anything with it, from posting to Slack, to calling a customer's own API, to shaping data no built-in action covers.
Who this is for: Custom Script is a technical, advanced capability. Writing or reviewing JavaScript, even the AI-generated version, is expected. If you are comfortable with the other automation actions but not with reading a bit of code, ask a technical teammate to set this one up, or stick to the built-in actions covered in https://trustpager.com/help-center/build-trigger-automations.
Custom Script always sits as a fixed card at the very bottom of every automation's action list, at https://app.trustpager.com/auto/automations. Unlike every other action, it cannot be dragged, reordered, or moved above another step. That is deliberate: the script runs asynchronously in the background, so nothing after it could ever meaningfully depend on its result, and it always needs to be last.
Turn it on with the Enable Custom Script toggle on that card, then click into it. The editor has two tabs: Configure, where you write or generate the code, manage secrets, and approve it for live use, and Test, where you run it against sample data before it ever touches a real record.
Both live on the Configure tab.
Every script is a single exported function with four parameters:
export async function runScript(ctx, tp, http, secrets) {
// your code here
return { ok: true };
}
| Parameter | What it is |
|---|---|
ctx | The data that fired the automation. ctx.trigger holds trigger-specific fields (an email's subject or from_email, a form's answers, and so on). ctx.contact_id and ctx.deal_id give you the linked contact and opportunity IDs directly. |
tp | A client for calling TrustPager's own API as your workspace. Use await tp.get(path), await tp.post(path, body), or await tp.patch(path, body) for anything not covered below. There are also shortcuts that hand you the actual record directly, no path needed: await tp.contact(), await tp.deal(), await tp.ourCompany() (your own workspace's profile), await tp.employer() (the contact's first linked employer, or null), await tp.employers() (all of them, if there's more than one), await tp.products() (the opportunity's attached products), and await tp.suppliers() (every supplier attached to those products, deduplicated). If you want several of these at once, await tp.context() hands you all of them together: { contact, deal, ourCompany, employer, products, suppliers }. |
http | A fetch-like helper for calling any other website or API - Slack, Stripe, a customer's own webhook, anything outside TrustPager. Call it the same way you'd call fetch(): await http(url, init), then check .ok and call .json() or .text() on what comes back. |
secrets | Third-party API keys or tokens you have saved against this script. Reference a saved secret in code as secrets.SECRET_NAME. |
While writing or reviewing code in the editor, click any of the reference chips above the code box (Contact ID, Opportunity ID, and the current trigger's fields) to insert the exact ctx. path for that value.
A script can read contacts, opportunities, companies, and products, and can create or read tasks. If a script tries to do anything beyond that, the call will fail with a permission error rather than silently doing nothing. If you hit one and need it lifted, flag it to us and we can extend what that script is allowed to touch.
If your script needs a third-party API key (a Slack webhook token, a Stripe key, anything that is not TrustPager's own API), add it in the Secrets this script needs panel inside the Custom Script editor. Give it a name and a value, then reference it in code as secrets.NAME.
Secrets are write-only: once saved, the value is never shown again, only the name. To change a value later, click the pencil next to it and enter a new one - you cannot view what is currently saved. This is the same "shown once" handling TrustPager uses for API keys, applied to your own third-party secrets.
Note: you never need to add TrustPager's own API key here. The tp client is already authenticated as your workspace - the secrets panel is only for keys to OTHER services.
Switch to the Test tab and click Run test to run the script once against sample data. This is a safe dry run: any reads the script performs (fetching data via tp or http) happen for real, but any writes are simulated rather than actually changing a record or sending a message. The result panel shows whether the test succeeded, any return value, console output, and the full error if it failed.
Testing does not require approval and does not affect real data, so it always works the same way regardless of whether the current code has been approved for live execution yet. Run it as many times as you like while you get the script right.
Back on the Configure tab, next to the code editor, you'll see a status chip: Awaiting approval until you review and click Approve for live execution, then Approved. Before a script can run for real, its code must be explicitly approved once. This is a separate, deliberate gate from just saving it: it is literal code that will execute automatically, so TrustPager will not run it live until you have reviewed it and approved it.
Two things both need to be true before Custom Script actually runs when the automation fires:
Editing the code again after approval, whether AI-regenerated or hand-edited, resets it back to awaiting approval. It needs approving again before it runs live. This protects you from a script silently changing behaviour without a second look.
When the automation fires, Custom Script runs after every other action in the list has completed. Because it runs asynchronously, the automation run sits in an "awaiting callback" state briefly until the script finishes and reports back success or failure.
If a script ever hangs, or its underlying process fails to report back at all (a crash, a network partition, anything that stops the normal callback), TrustPager automatically marks that run as timed out after 10 minutes rather than leaving it stuck forever. You do not need to do anything about this: it is just a safety net. If you see a run marked as timed out in your automation history, the script did not finish and report back in time, and it is worth a look at what the script was trying to do.
Check https://trustpager.com/help-center/automation-execution-logs to see every run, including Custom Script results, errors, and timeouts. Turn on Notify me if this fails inside the Custom Script editor to get notified whenever a run fails.
An automation triggered by Deal Value Updated, with a Custom Script description of "When this fires, post a message to our Slack webhook with the opportunity name and value", generates code roughly like:
export async function runScript(ctx, tp, http, secrets) {
const deal = await tp.deal();
await http(secrets.SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `Opportunity "${deal.name}" is now worth $${deal.value}`,
}),
});
return { ok: true };
}
with SLACK_WEBHOOK_URL saved in the secrets panel.
Here's a second one that leans on tp.context() to pull everything about the trigger's contact and opportunity in one call, then emails a summary to the assigned rep:
export async function runScript(ctx, tp, http, secrets) {
const { contact, deal, employer } = await tp.context();
const summary = employer
? `${contact.first_name} ${contact.last_name} (${employer.name}) - opportunity "${deal.name}" worth $${deal.value}`
: `${contact.first_name} ${contact.last_name} - opportunity "${deal.name}" worth $${deal.value}`;
await tp.post('/tasks', {
title: `Follow up: ${deal.name}`,
description: summary,
deal_id: deal.id,
contact_id: contact.id,
});
return { ok: true };
}
One call to tp.context() instead of separate tp.contact(), tp.deal(), and tp.employer() calls - use it whenever a script needs more than one of these at once.
Tip: start simple. A script that reads one record and makes one outbound call is easier to test, approve, and debug than one that tries to do five things at once. Add complexity once the basic version is working and approved.
Talk to Evie right now, or book a real human for a deeper walkthrough. Whichever feels right.
Hear her in 10 seconds.

60 minutes with a real human · Google Meet · Free.