Skip to content

Webhook payload structure

How webhook payloads work

When Tallyfy sends a webhook to your endpoint, it delivers a JSON payload with data about the event. The structure depends on the webhook type - process-level webhooks (fired when a process launches or completes) versus task-level webhooks (fired when a specific task completes).

This reference shows the actual structure produced by Tallyfy’s API code, not theoretical examples.

Process-level webhook payload

Process-level webhooks fire when someone launches a new process or when a process completes. They include the full process state and all kick-off form data.

Top-level structure

Process Webhook Payload
├── Process information (from RunTransformer)
│ ├── id, increment_id, name, summary
│ ├── status, progress, whole_progress
│ ├── checklist_id, checklist_title
│ ├── created_at, started_at, completed_at
│ ├── owner_id, started_by
│ ├── collaborators (array of user IDs)
│ └── Kick-off form data
│ ├── prerun (structured form field values)
│ ├── prerun_status, prerun_completed_at
│ └── prerun_submitted_by
├── process-tasks
│ └── Map of task alias => task ID
├── ko-form-fields
│ └── Map of field alias => field value
├── guest-links
│ └── Map of guest email => {link_for_guest: URL}
└── launcher
└── User who launched the process

Complete example

{
"id": "abc123def456ghi789jkl012mno345pq",
"increment_id": 1234,
"checklist_id": "template789xyz123abc",
"checklist_title": "Vendor Contract Review Workflow",
"name": "Vendor Contract Review - ACME Corp",
"summary": "Complete vendor contract review and approval workflow",
"status": "in-progress",
"progress": 60,
"whole_progress": 60,
"started_by": "user123abc456",
"owner_id": "user123abc456",
"prerun": {
"vendor-name": "Widget Suppliers Inc",
"contract-type": "Service Agreement",
"estimated-value": "50000",
"urgency": "normal"
},
"prerun_status": "completed",
"prerun_completed_at": "2025-10-01T09:15:00Z",
"prerun_completed_by": "user123abc456",
"prerun_length": 4,
"created_at": "2025-10-01T09:00:00Z",
"started_at": "2025-10-01T09:15:00Z",
"last_updated": "2025-10-02T14:30:00Z",
"completed_at": null,
"collaborators": ["user123", "user456", "user789"],
"late_tasks": 0,
"due_soon": true,
"due_date": "2025-10-15T17:00:00Z",
"users": [
{"user_id": "user123", "full_name": "John Smith"},
{"user_id": "user456", "full_name": "Jane Doe"}
],
"groups": [
{"group_id": "group123", "name": "Legal Team"}
],
"process-tasks": {
"initial-review": "task001abc",
"legal-review": "task002def",
"review-contract": "task003ghi",
"execute-contract": "task004jkl"
},
"ko-form-fields": {
"vendor-name": "Widget Suppliers Inc",
"contract-type": "Service Agreement",
"estimated-value": "50000",
"urgency": "normal"
},
"guest-links": {
"external.reviewer@vendor.com": {
"link_for_guest": "https://go.tallyfy.com/guest-all-task/org789xyz123/guestcode123abc"
},
"compliance@vendor.com": {
"link_for_guest": "https://go.tallyfy.com/guest-all-task/org789xyz123/guestcode456def"
}
},
"launcher": {
"id": "user123abc456",
"email": "john@acmecorp.com",
"full_name": "John Smith"
}
}

Task-level webhook payload

Task-level webhooks fire when a specific task completes. They contain detailed info about the completed task, all process data collected so far, and the next task that needs attention.

What’s next_task? It’s the first visible incomplete task ordered by position number. “Visible” means not auto-skipped by automation rules. “Incomplete” means not yet completed (includes tasks that are in-progress). Returns an empty array [] if all tasks in the process are done.

Top-level structure

Task Webhook Payload
├── this_task
│ ├── id, title, alias, status, summary
│ ├── deadline, position
│ ├── completed_at, completed_by
│ ├── owners (users array + guests array)
│ ├── captures (form fields in THIS task)
│ │ └── {timeline_id: {alias, label, value}}
│ └── assets (files uploaded to this task)
├── launcher
│ └── User who launched the process
├── completer
│ └── User who completed this task
├── process-tasks
│ └── Map of task alias/id => task ID
├── form-fields
│ └── ALL form fields from ALL tasks (flat structure)
│ └── {field_alias: value}
├── template
│ └── id, title, alias, summary (NO version field)
├── process
│ ├── id, organization_id, name, summary
│ ├── status, progress, created_at
│ ├── ClientURL (link to process in Tallyfy)
│ ├── prerun (kick-off form values)
│ ├── process_forms (all fields by step_id)
│ │ └── {step_id: {timeline_id: {alias, label, value}}}
│ └── tags
├── guest-links
│ └── Map of guest email => {link_for_guest: URL}
└── next_task
├── id, title, alias, status
├── summary, position, deadline
├── owners (users + guests)
└── captures (empty until task is worked on)

Complete example

{
"this_task": {
"id": "task003ghi789xyz",
"title": "Review Contract",
"alias": "review-contract",
"status": "completed",
"summary": "Review the vendor contract for accuracy",
"deadline": "2025-10-15T17:00:00Z",
"position": 3,
"completed_at": "2025-10-02T14:30:00Z",
"completed_by": {
"id": "user123",
"email": "john@acmecorp.com",
"full_name": "John Smith"
},
"captures": {
"capture123timeline": {
"alias": "contract-value",
"label": "Contract Value",
"value": "50000"
},
"capture456timeline": {
"alias": "approval-notes",
"label": "Approval Notes",
"value": "All terms look good. Approved for signature."
}
},
"owners": {
"users": [
{
"id": "user123",
"email": "john@acmecorp.com",
"full_name": "John Smith"
}
],
"guests": [
"external.reviewer@vendor.com"
]
},
"assets": [
{
"id": "asset123",
"filename": "signed-contract.pdf",
"version": "1",
"public_link": "https://api.tallyfy.com/organizations/org789/file/asset123/dl"
}
]
},
"launcher": {
"id": "user123",
"email": "john@acmecorp.com",
"full_name": "John Smith"
},
"completer": {
"id": "user123",
"email": "john@acmecorp.com",
"full_name": "John Smith"
},
"process-tasks": {
"initial-review": "task001abc",
"legal-review": "task002def",
"review-contract": "task003ghi",
"task004jkl": "task004jkl"
},
"form-fields": {
"initial-review-notes": "Contract appears standard. Needs legal review.",
"risk-assessment": "Low",
"legal-review-status": "Approved",
"contract-value": "50000",
"approval-notes": "All terms look good. Approved for signature.",
"approval-date": "2025-10-02"
},
"template": {
"id": "template123abc",
"title": "Vendor Contract Review Workflow",
"alias": "vendor-contract-review",
"summary": "Standard workflow for reviewing and approving vendor contracts"
},
"process": {
"id": "run123abc456",
"organization_id": "org789xyz123",
"name": "Vendor Contract Review - ACME Corp",
"summary": "Complete vendor contract review and approval workflow",
"status": "in-progress",
"progress": 60,
"created_at": "2025-10-01T09:00:00Z",
"ClientURL": "https://go.tallyfy.com/tracker/run123abc456",
"prerun": {
"vendor-name": "Widget Suppliers Inc",
"contract-type": "Service Agreement",
"estimated-value": "50000"
},
"process_forms": {
"step001": {
"capture001timeline": {
"alias": "initial-review-notes",
"label": "Initial Review Notes",
"value": "Contract appears standard."
}
},
"step002": {
"capture002timeline": {
"alias": "legal-review-status",
"label": "Legal Review Status",
"value": "Approved"
}
}
},
"tags": [
{
"id": "tag123",
"name": "High Priority",
"color": "#FF6B6B"
}
]
},
"guest-links": {
"external.reviewer@vendor.com": {
"link_for_guest": "https://go.tallyfy.com/guest-all-task/org789xyz123/guestcode123abc"
},
"compliance@vendor.com": {
"link_for_guest": "https://go.tallyfy.com/guest-all-task/org789xyz123/guestcode456def"
}
},
"next_task": {
"id": "task004jkl",
"title": "Execute Contract",
"alias": "execute-contract",
"status": "not-started",
"summary": "Send final contract for execution",
"position": 4,
"deadline": "2025-10-20T17:00:00Z",
"captures": {},
"owners": {
"users": [
{
"id": "user789",
"email": "legal@acmecorp.com",
"full_name": "Legal Team"
}
],
"guests": []
}
}
}

The guest-links object provides direct access URLs for all guest assignees in a process. You can use it to send personalized emails or SMS notifications to guests with direct links to their assigned tasks.

Structure

"guest-links": {
"guest.email@company.com": {
"link_for_guest": "https://go.tallyfy.com/guest-all-task/{org_id}/{guest_code}"
}
}

Key details

  • Included in both webhook types - process-level and task-level
  • All unique guests - every unique guest assigned anywhere in the process
  • Direct access - links bypass login, so guests click and see their tasks
  • Persistent codes - guest codes stay stable across the process lifecycle

Common use cases

  • Custom email notifications - build your own notification system using Zapier or Make
  • SMS integration - send task reminders via Twilio with direct access links
  • CRM integration - update external CRM records with guest task links
  • Client portals - embed guest task links in customer-facing dashboards

Form field structures

Form fields appear in webhooks using different structures depending on context.

In this_task.captures - detailed structure

Task-specific form fields use the complete structure with timeline IDs as keys:

"captures": {
"capture_timeline_123": {
"alias": "contract-value",
"label": "Contract Value",
"value": "50000"
},
"capture_timeline_456": {
"alias": "approval-notes",
"label": "Approval Notes",
"value": "All terms look good."
}
}

In form-fields - flat structure

All process form fields use a simplified flat structure with aliases as keys:

"form-fields": {
"contract-value": "50000",
"approval-notes": "All terms look good.",
"initial-review-notes": "Contract appears standard.",
"legal-review-status": "Approved"
}

In process.process_forms - organized by step

Form fields grouped by which step they belong to:

"process_forms": {
"step001": {
"capture_timeline_001": {
"alias": "initial-review-notes",
"label": "Initial Review Notes",
"value": "Contract appears standard."
}
},
"step002": {
"capture_timeline_002": {
"alias": "legal-review-status",
"label": "Legal Review Status",
"value": "Approved"
}
}
}

Date field formatting

Date fields in captures respect your organization’s webhook date format setting. Tallyfy supports two formats:

ISO 8601 format (default):

"start-date": {
"alias": "start-date",
"label": "Contract Start Date",
"value": "2025-10-15"
}

Localized format (if configured as d/m/Y h:i A T):

"start-date": {
"alias": "start-date",
"label": "Contract Start Date",
"value": "15/10/2025 09:00 AM UTC"
}

This format applies to all date fields across the webhook payload.

Task assignment info

Task owners appear in the owners object containing both organization members and external guests.

"owners": {
"users": [
{
"id": "user123",
"email": "john@acmecorp.com",
"full_name": "John Smith"
},
{
"id": "user456",
"email": "jane@acmecorp.com",
"full_name": "Jane Doe"
}
],
"guests": [
"external.reviewer@vendor.com",
"compliance@vendor.com"
]
}

Important differences:

  • users contains full objects with ID, email, and name
  • guests contains only email addresses as strings
  • Use guest-links to get access URLs for guests

Common integration patterns

Sending custom emails to guests

Use the guest-links object to send personalized emails:

// In your webhook handler (e.g., Zapier, Make, n8n)
const guestLinks = webhookPayload['guest-links'];
for (const [email, linkData] of Object.entries(guestLinks)) {
sendEmail({
to: email,
subject: `New task assigned: ${webhookPayload.this_task.title}`,
body: `You have a new task. Click here to access: ${linkData.link_for_guest}`
});
}

Extracting all collected data

Get all form data collected across the entire process:

// Flat structure - simplest for most integrations
const allFormData = webhookPayload['form-fields'];
// Example: { "contract-value": "50000", "approval-notes": "..." }
// Or get kick-off form data specifically
const kickoffData = webhookPayload.process.prerun;
// Example: { "vendor-name": "Widget Suppliers Inc", ... }

Triggering follow-up actions

Use the next_task object to prepare for upcoming steps:

const nextTask = webhookPayload.next_task;
// next_task is an empty array [] when all tasks are done
if (nextTask && nextTask.title === "Execute Contract") {
// Prepare contract execution system
// Notify legal team
// Update external tracking system
}

Webhooks > Webhook scenarios

Tallyfy webhooks automatically push JSON data to external systems when a process launches or completes or when a specific task is finished and the payload carries all accumulated form fields and process metadata up to that point so you can connect workflows to spreadsheets CRMs email tools and other apps through middleware like Zapier or Make.com without polling.

Webhooks > Details about webhooks

Tallyfy webhooks automatically POST JSON data to external URLs you define whenever workflow events like step completions or process launches occur and can be configured at either the template level for broad monitoring or the step level for targeted triggers with built-in security through the X-Tallyfy-orgID header.

Integrations > Webhooks

Tallyfy webhooks automatically send structured JSON data to any external system via HTTP POST requests whenever workflow events like task completions or process launches occur — eliminating the need for constant polling and enabling instant real-time integrations with middleware platforms or custom APIs and serverless functions.

Webhooks > Send emails using webhooks

Tallyfy webhooks can be connected to middleware tools like Zapier or Make to automatically send custom emails with full process context whenever specific workflow events like task completions occur.