PeopleForce

Employee custom table rows

Webhook topics and example payloads for rows in employee custom tables.

Custom tables are the repeating tables you define yourself on an employee's profile — certifications, equipment, dependants, and so on. Each row emits its own event, so an integration can stay in step without polling.

The custom table row emits these webhooks:

  • employee_table_row_create — a row is added
  • employee_table_row_update — a row is changed
  • employee_table_row_destroy — a row is deleted

Subscribe to each one independently. Selecting one does not subscribe you to the other two, and existing endpoints gain none of them until you add them.

Every payload is self-contained: it carries the employee, the table, and every column with its current value, so you do not need a follow-up API call. Values are the current state only — there is no before-and-after comparison.

When they fire

A delivery is sent when the row is saved or deleted through any of these paths:

  • the employee's profile
  • the Public API (POST / PATCH / DELETE on an employee's table rows)
  • an approved employee change request that adds a row — this emits employee_table_row_create

One event is sent per changed row, so a change request touching three rows sends three deliveries.

When they do not fire

These topics cover custom tables only. Rows in PeopleForce system tables — position, salary, employment history, and compliance tables — never emit them.

Nothing is sent when:

  • the save changes no stored value, so an identical update is silent
  • the create, update, delete, or approval fails
  • a change request is submitted, rejected, or cancelled — only approval emits
  • a row is created by a hire form or preboarding form

Employee custom table row created

Action: employee_table_row_create.

{
  "action": "employee_table_row_create",
  "data": {
    "id": 88213,
    "employee": {
      "id": 133710,
      "employee_number": "IT-1029",
      "first_name": "Ada",
      "last_name": "Lovelace",
      "email": "ada@example.com"
    },
    "table": {
      "id": 4471,
      "name": "Certifications",
      "internal_name": "certifications"
    },
    "columns": [
      {
        "id": 90114,
        "name": "Level",
        "internal_name": "level",
        "type": "EmployeeTableColumns::Text",
        "value": "Professional"
      },
      {
        "id": 90115,
        "name": "Certified on",
        "internal_name": "certified_on",
        "type": "EmployeeTableColumns::Date",
        "value": null
      },
      {
        "id": 90116,
        "name": "Score",
        "internal_name": "score",
        "type": "EmployeeTableColumns::Number",
        "value": "87"
      },
      {
        "id": 90117,
        "name": "Renewable",
        "internal_name": "renewable",
        "type": "EmployeeTableColumns::CheckBox",
        "value": "1"
      },
      {
        "id": 90118,
        "name": "Rating",
        "internal_name": "rating",
        "type": "EmployeeTableColumns::SingleSelect",
        "value": { "id": "3312", "name": "Excellent" }
      },
      {
        "id": 90119,
        "name": "Topics",
        "internal_name": "topics",
        "type": "EmployeeTableColumns::MultipleSelect",
        "value": [
          { "id": "3315", "name": "Ruby" },
          { "id": "3316", "name": "Rails" }
        ]
      },
      {
        "id": 90120,
        "name": "Mentor",
        "internal_name": "mentor",
        "type": "EmployeeTableColumns::Reference",
        "value": { "id": "133711", "name": "Grace Hopper" }
      },
      {
        "id": 90121,
        "name": "Notes",
        "internal_name": "notes",
        "type": "EmployeeTableColumns::LongText",
        "value": "Renewal due next year"
      }
    ],
    "meta": {
      "created_at": "2026-08-17T09:41:12.204Z",
      "updated_at": "2026-08-17T09:41:12.204Z"
    }
  }
}

Employee custom table row updated

Action: employee_table_row_update. The payload shape matches employee_table_row_create, with the row's values after the change.

Employee custom table row deleted

Action: employee_table_row_destroy. The payload shape matches employee_table_row_create, and carries the row's final values as they stood before deletion.

Field reference

FieldTypeNotes
data.idintegerThe row ID.
data.employeeobjectEmployee identity: id, employee_number, first_name, last_name, and email.
data.tableobjectThe custom table: id, name, and internal_name.
data.columnsarrayEvery column on the table, in the order shown in the product. Deleted columns are excluded.
data.columns[].idintegerThe column ID.
data.columns[].internal_namestringThe stable API name. Prefer it over name, which admins can rename freely.
data.columns[].typestringThe column type — see the table below.
data.columns[].valuevariesThe current value — see the table below.
data.metaobjectcreated_at and updated_at for the row.

Column values by type

Every column on the table appears in columns, whether or not it holds a value. An empty column is present with a null value.

typevalue shapeEmpty
EmployeeTableColumns::Textstringnull
EmployeeTableColumns::Numberstring, e.g. "87"null
EmployeeTableColumns::Datestring, e.g. "2026-08-14"null
EmployeeTableColumns::CheckBoxstring, "1" when ticked or "0" when notnull
EmployeeTableColumns::LongTextstring, HTML stripped to plain textnull
EmployeeTableColumns::SingleSelect{ "id": "…", "name": "…" }null
EmployeeTableColumns::MultipleSelectarray of { "id": "…", "name": "…" }[]
EmployeeTableColumns::Reference{ "id": "…", "name": "…" } — an employeenull

Four details to code against:

  • Scalars are JSON strings, not JSON numbers or booleans. A number column sends "87", a date sends "2026-08-14", and a checkbox sends "1" or "0". Both write paths store the value as text and the payload returns it uncast, so parse rather than assume. Rows loaded by an older import may still hold a raw JSON number.
  • An unticked checkbox sends "0", which is a real value, not an empty one. Only a checkbox that was never set sends null.
  • A multi-select is always an array, never null. An empty one is [].
  • The id inside a select, multi-select, or reference value is a string, because that is how the row stores it. The surrounding columns[].id and data.id are integers.

A reference value keeps its stored id even when the referenced employee can no longer be found, in which case name is null.

Testing a payload

The sample-payload endpoint (GET /webhooks?key=<topic>) does not yet cover the three custom table row topics and returns 204 No Content for them. To see a real payload, subscribe an endpoint, change a row, then open the delivery in Settings → Webhooks → delivery history.

On this page