Setup
Create the action in Zudo
- Go to Settings → Connections.
- Find your Vitally connection and open it.
- Under Playbook Webhook Actions, click + Add Action.
- Fill in the fields:
- Name — a descriptive label (e.g., “Auto-Renewal Handler”)
- Description — what this action does (optional but recommended)
- JavaScript Code — your custom logic (see Writing Your Script below)
- Enabled — toggle to activate or deactivate the action
- Save the action.
Copy the webhook URL
After saving, each action gets a unique webhook URL. Click the copy button next to the action to copy it.The URL format is:Keep this URL — you’ll paste it into Vitally in the next step.
Configure the Vitally Playbook
- In Vitally, create or open the Playbook you want to use.
- Add a Webhook action step.
- Paste your webhook URL into the URL field.
- Set your trigger conditions as needed.
- Activate the Playbook.
Writing Your Script
Your script runs in a secure JavaScript sandbox with two input objects. It must return either a trait update object ornull.
Available Variables
account
The full Vitally account object, fetched fresh at the time the webhook fires. Use this for the latest trait values.
payload
The raw webhook payload sent by Vitally. It contains the playbook context and a snapshot of the account at the time of the trigger.
account object nests traits under account.traits.key, but the payload object puts traits directly on payload.match.account.key. When accessing the same trait from either source:
account when you need the most up-to-date value. Use payload when you need the value as it was at the moment the Playbook triggered.
Helper Functions
The sandbox includes built-in helpers for common date and interval operations.Date Helpers
| Function | Description | Example |
|---|---|---|
addMonths(date, months) | Add months to a date | addMonths("2026-01-31", 1) → "2026-02-28" |
addDays(date, days) | Add days to a date | addDays("2026-01-15", 30) → "2026-02-14" |
addYears(date, years) | Add years to a date | addYears("2026-01-15", 1) → "2027-01-15" |
today() | Get today’s date as a string | today() → "2026-02-04" |
now() | Get current datetime (ISO) | now() → "2026-02-04T12:30:45.123Z" |
parseDate(date) | Parse any date to ISO format | parseDate("Jan 15, 2026") → "2026-01-15" |
isPast(date) | Check if a date is in the past | isPast("2025-01-01") → true |
isFuture(date) | Check if a date is in the future | isFuture("2027-01-01") → true |
diffDays(date1, date2) | Days between two dates | diffDays("2026-01-01", "2026-01-15") → 14 |
Interval Helpers
| Function | Description | Example |
|---|---|---|
intervalToMonths(interval) | Convert interval name to months | intervalToMonths("annual") → 12 |
addInterval(date, interval) | Add a named interval to a date | addInterval("2026-01-15", "annual") → "2027-01-15" |
addInterval and intervalToMonths:
| Interval name | Months |
|---|---|
monthly, month to month | 1 |
quarterly | 3 |
semi-annual, semiannual, bi-annual | 6 |
annual, yearly | 12 |
biennial, 2-year | 24 |
triennial, 3-year | 36 |
Returning a Result
Your script must return one of two things:Update traits
Return an object with atraits key:
current_contract_expiration). Values can be strings, numbers, booleans, or ISO date strings.
Skip the update
Returnnull when you don’t want to make any changes:
null when conditions aren’t met, required data is missing, or you want to skip a particular trigger.
Code Examples
Auto-Renewal: Calculate Next Expiration Date
Use this to automatically advance the contract expiration date when a subscription renews.Set a Date Trait to Today
Use this to stamp a date trait whenever a Playbook fires — useful for audit trails.Conditional Update
Only update a trait when a specific condition is true; skip otherwise.Calculate Days Until Renewal
Compute a numeric trait from an existing date trait.Troubleshooting
| Error | Cause | Solution |
|---|---|---|
Webhook action not found | Invalid token in the URL | Check that the webhook URL is correct and hasn’t changed |
Action is disabled | The action’s toggle is off | Enable the action in Settings → Connections |
Connection is disabled | The Vitally connection is off | Enable the Vitally connection in Connections |
Missing account in payload | Malformed Vitally webhook | Verify the Playbook webhook step is configured correctly |
Execution timed out | Your script took more than 5 seconds | Simplify the logic; remove any loops or heavy operations |
Invalid date: ... | A bad value was passed to a date helper | Check that the date string is valid before passing it |
- Use
console.login your script — logs appear in the execution stats view for the action. - Check the action card in Settings for trigger count, error count, and the last error message.
- Start with
{ traits: { test_trait: today() } }to confirm the webhook fires before adding complex logic. - The code editor validates syntax before saving, but runtime errors only surface after a real trigger.
Best Practices
- Validate inputs before using them — check for
nullorundefinedbefore accessing nested properties. - Return
nullinstead of throwing when required data is missing. - Prefer
account.traitsfor the latest values;payloadmay be slightly stale. - One action per Playbook trigger is easier to debug than one script that does everything.
- Don’t log full account objects or traits that contain PII.
- Test in staging before activating in production.
