Webhook payloads & delivery reference

Subscribe to an event with POST /v1/webhooks (event_code + hook_url) and OnRamp will POST JSON to your URL whenever it happens. This page is the contract: the envelope every event shares, the payload for each event, and the delivery behavior your receiver must assume.

The envelope

Every delivery has the same top-level shape; only payload varies by event.

{
  "event_id": 10,
  "event_name": "task_completed",
  "object_code": "TASK",
  "object_name": "Sign security review",
  "timestamp": "2026-07-23T14:31:07Z",
  "modified": {
    "modified_at": "2026-07-23T14:31:07Z",
    "modified_by": {
      "first_name": "Dana", "last_name": "Cruz", "full_name": "Dana Cruz",
      "email_address": "dana@customer.example",
      "vendor_name": "Acme Health", "is_customer": true, "is_active": true
    }
  },
  "payload": { "…per-event, see below…": {} }
}
Field Meaning
event_id Numeric id of the event type (not unique per delivery — don't dedupe on it)
event_name The event type_code, e.g. task_completed — same value you subscribed with
object_code What kind of object: PROJECT, TASK, SUBTASK, MODULE, COMMENT
object_name Display name of the object
timestamp When the event happened (ISO-8601)
modified.modified_by Who did it — note is_customer distinguishes customer vs internal users
payload The event-specific data — tables below
Delivery behavior — build your receiver around this
  • At-least-once. The same event can be delivered more than once. Dedupe on the combination of event_name + the object's uuid + timestamp before acting on anything.
  • Ordered within your org. Events for your organization are delivered in the order they occurred, one at a time. A slow receiver therefore delays everything behind it — return 200 fast (well under 10 seconds) and do slow work asynchronously.
  • Fetch-on-event. Deliveries are not yet signed; anyone who finds your URL can POST fake JSON at it. Treat the delivery as a notification, take the uuid, and re-fetch the object through the API with your key before acting.
  • Have a safety net. Webhooks complement — never replace — a periodic reconciliation sweep (see the mirror & delta-sync recipe). Some changes don't emit a public event, and no delivery pipeline is perfect.

⚠️ Retry policy — pending verification (ONRAMP-5864). Failed deliveries are re-attempted a limited number of times before being set aside; the exact attempt count and backoff are being confirmed and will be documented here. Design as if a delivery can be missed — that's what the reconciliation sweep is for.

Deprecated fields

Payloads currently include legacy integer-id fields (id, project_id, integer_id, replied_to_comment_id, …) alongside their UUID equivalents. They are removed on July 31, 2026. Build only against the *_uuid / uuid fields shown below.


Project events

project_created · project_updated · project_completed · project_archived

payload.project:

Field Type Notes
project_uuid string The project's UUID (will be renamed uuid on Jul 31, 2026)
name, description string
status string Status name (in the REST API, status is an {id, name} object)
owner_name string
created_date, completed_date, archived_date string
completed_by_name, archived_by string
completion_progress, customer_completion_progress string Percentages as strings
project_duration string
portal_project_url, internal_project_url string Deep links (customer portal / your dashboard)
value number | null Project value
account object | null The customer account
data_fields array {uuid, name, value} custom fields
tags array
external_objects array External links attached to the project

project_invitation differs: payload is {project_uuid, project_name, invited_user: {uuid, first_name, last_name, full_name, customer_name, vendor_name, is_customer, email_address, user_role}}.

Task events

task_created · task_updated · task_completed · task_deleted

payload:

Field Type Notes
project_uuid, project_name string
parent_module_uuid, parent_module_name string
project_completed_percentage string
next_task_to_complete object | null
portal_project_url, internal_project_url string
task object The task itself — below

payload.task:

Field Type Notes
uuid, name, detail, status string status is the status name
due_date, created_date, scheduled_start_date, started_date, completed_date string
assignee_name, created_by_name, modified_by_name, completed_by_name string
is_internal, is_archived, is_non_sequential bool
module_uuid, module_name, display_order string/int | null Placement; null when unknown
subtasks_answers array | null {key, name, answer} — form answers collected on the task
data_fields array {uuid, name, value}
tags array

subtasks_answers on task_completed is how completion-gated integrations harvest what the customer filled in — see the provisioning recipe.

Subtask events

subtask_created · subtask_updated · subtask_completed · subtask_deleted

payload: {project_uuid, project_name, parent_task_uuid, parent_task_name, subtask} where payload.subtask:

Field Type Notes
uuid, name, description string
parent_task_uuid, parent_subtask_uuid string
status_name string
display_order int
configuration array Subtask type configuration
assignee_name string
action_response string The answer/value the customer submitted
answered_by_user_name, answered_date string
Comment events

comment_new · comment_reply · comment_mention · comment_updated · comment_deleted

payload.comment:

Field Type Notes
uuid string Dedupe key for comment events
created_at, edited_at string
created_by_name, created_by_uuid string Compare created_by_uuid to your API user to drop your own posts (echo-loop protection)
message_plain, message_rtf string
replied_to_comment_uuid string | null Set on threaded replies
associated_object_type, associated_object_uuid string The task the comment is on
parent_object_type, parent_object_uuid string The project

The comment payload also carries created_by_user_id and vendor_id — internal integer fields kept for compatibility. Do not build on them; they are slated for removal.

Module events

module_created · module_updated · module_deleted

payload: {project_uuid, project_name, module: {uuid, name, customer_portal_name, is_archived, created_date, created_by_user_name, modified_by_user_name, modified_date, completion_progress}}.


Everything else

The live catalog — including account-note events and anything added after this page — is always GET /v1/events. Subscribe with the type_code you see there. If an event you need is missing a payload example here, ask support@onramp.us and we'll add it.