Skip to main content
The Vitally integration pulls your customer accounts into Zudo and keeps them in sync. You can also automate trait updates using webhook actions triggered by Vitally Playbooks.

How to connect

1

Open Connections

Go to Settings → Connections and click Add Connection.
2

Select Vitally

Choose Vitally from the list and enter your Vitally API credentials.
3

Sync your data

Once connected, click Sync Now to pull your accounts into Zudo. Syncs also run automatically on a schedule.

What syncs

  • Accounts — your Vitally accounts appear in Zudo with name, MRR, and other standard fields
  • Account traits — choose which custom traits to import in the connection settings under Custom Account Traits
  • Custom objects — additional Vitally data associated with accounts
To control which custom traits sync, expand your Vitally connection card and use the Custom Account Traits panel to toggle individual traits on or off.

Webhook automations

Webhook actions let you run custom JavaScript when a Vitally Playbook fires. Use them to automatically update account traits based on events — like advancing a renewal date when a subscription renews, or stamping a trait with today’s date when a Playbook condition is met.

Setting up a webhook action

1

Create the action in Zudo

Go to Settings → Connections, expand your Vitally connection, and find the Playbook Webhook Actions section. Click + Add Action.Fill in:
  • Name — a descriptive label (e.g., “Auto-Renewal Handler”)
  • Description — optional, what the action does
  • JavaScript code — your automation logic (see examples below)
  • Enabled — toggle to activate or pause the action
2

Copy the webhook URL

Each action gets a unique URL. Click the copy button on the action card to copy it.
3

Configure your Vitally Playbook

In Vitally, open or create a Playbook and add a Webhook step. Paste the URL from Zudo, set your trigger conditions, and activate the Playbook.

The JavaScript sandbox

Your code runs in a secure sandbox. Two variables are available: account — the full Vitally account fetched fresh from the API at the time of the webhook call. Access traits via account.traits:
const expDate = account.traits?.current_contract_expiration;
const interval = account.traits?.billing_interval;
payload — the raw webhook payload sent by Vitally. Note that traits appear at the top level of payload.match.account, not nested under .traits:
// Traits from payload are top-level on payload.match.account
const expFromPayload = payload.match?.account?.current_contract_expiration;
Use account.traits when you need the most up-to-date values. Use payload.match.account when you need the trait values at the moment the Playbook was triggered.

Helper functions

The sandbox includes built-in helpers so you don’t need to write date math from scratch.

Date helpers

FunctionWhat it doesExample
addMonths(date, months)Add months to a dateaddMonths("2026-01-31", 1)"2026-02-28"
addDays(date, days)Add days to a dateaddDays("2026-01-15", 30)"2026-02-14"
addYears(date, years)Add years to a dateaddYears("2026-01-15", 1)"2027-01-15"
today()Today’s date as an ISO stringtoday()"2026-02-04"
now()Current datetime as an ISO stringnow()"2026-02-04T12:30:45.123Z"
parseDate(date)Parse any date string to ISO formatparseDate("Jan 15, 2026")"2026-01-15"
isPast(date)Check if a date is in the pastisPast("2025-01-01")true
isFuture(date)Check if a date is in the futureisFuture("2027-01-01")true
diffDays(date1, date2)Days between two datesdiffDays("2026-01-01", "2026-01-15")14

Interval helpers

FunctionWhat it doesExample
intervalToMonths(interval)Convert an interval name to a number of monthsintervalToMonths("annual")12
addInterval(date, interval)Add a named interval to a dateaddInterval("2026-01-15", "annual")"2027-01-15"
Supported interval names: monthly, month to month, quarterly, semi-annual, semiannual, bi-annual, annual, yearly, biennial, 2-year, triennial, 3-year.

Return format

Return { traits: { key: value } } to update traits on the account in Vitally. Return null to skip the update for this trigger.
// Update traits
return {
  traits: {
    current_contract_expiration: "2027-01-15",
    last_auto_renewal_at: now(),
  },
};

// Skip this trigger
return null;
Trait keys should match the trait path shown in the Available Traits panel inside the action editor. Click any trait in that panel to copy its path.

Code examples

Auto-renewal: calculate next expiration date

const currentExp =
  account.traits?.current_contract_expiration ||
  payload.match?.account?.current_contract_expiration;

const interval =
  account.traits?.billing_interval ||
  payload.match?.account?.billing_interval ||
  "annual";

if (!currentExp) {
  console.warn("No expiration date found, skipping");
  return null;
}

const newExpiration = addInterval(currentExp, interval);

return {
  traits: {
    current_contract_expiration: newExpiration,
    last_auto_renewal_at: now(),
  },
};

Set a date trait to today

return {
  traits: {
    last_contacted_at: today(),
  },
};

Conditional update

const autoRenew =
  account.traits?.auto_renew || payload.match?.account?.auto_renew;

if (autoRenew === true) {
  return {
    traits: {
      last_auto_renew_check: today(),
    },
  };
}

return null; // Skip if auto_renew is not true

Common errors

ErrorCauseFix
Webhook action not foundThe URL token doesn’t match any actionCheck that you copied the correct webhook URL
Action is disabledThe action is toggled offEnable the action in Settings → Connections
Connection is disabledThe Vitally connection itself is disabledRe-enable the Vitally connection
Missing account in payloadVitally sent a malformed webhookCheck your Playbook’s webhook configuration in Vitally
Execution timed outYour code took longer than 5 secondsSimplify your code and remove any loops
Invalid date: ...A bad date value was passed to a helperCheck that your date traits contain valid date strings
You can view execution history — including the request payload, traits updated, and any errors — by clicking the logs icon on an action card.

Security

  • Code runs in an isolated sandbox with no access to the filesystem, network, or external modules
  • Input objects (account and payload) are read-only
  • Execution times out after 5 seconds
  • Each action has a unique URL token; regenerate it by deleting and recreating the action