Event reference

[object Object]

Every webhook delivery shares the same envelope:

{
  "id": "8e3f5c44-1cd1-4a40-bb4f-9d4f95c54c1d",
  "type": "job.created",
  "createdAt": 1715520000,
  "subscriptionId": "7c2f8f7a-d8ab-4d4a-9a09-71b4e6dffb8a",
  "deliveryAttempt": 1,
  "data": { /* type-specific */ }
}
Envelope fieldTypeDescription
idstring (uuid)Unique event ID. Mirror to Jobven-Event-Id header. Use for dedupe.
typestringEvent type. See the sections below.
createdAtinteger (unix seconds)When the event was created on Jobven's side.
subscriptionIdstring (uuid)Your subscription ID.
deliveryAttemptinteger1 on the first attempt, increments for retries.
dataobjectType-specific payload (documented below).

job.created

A job that matches your filter was listed: it is new, or a closed job reopened. Its listedAt is the moment it was listed, the value the Pull API's listedAfter filters on.

{
  "type": "job.created",
  "data": {
    "job": { /* full Job object, see /docs/reference/jobs */ }
  }
}

The data.job shape matches the public Pull API Job object exactly. See Jobs API reference.

job.created can fire for a job you have already seen. If an employer relists a role we previously closed, it returns under the same id and we send job.created again rather than job.updated: you were told the job closed, so there is nothing to apply an update to. Handle this event as an upsert keyed on id, not as an insert.

job.updated

A job we've previously delivered to you changed. Only fires when at least one tracked field actually changed: title, description, salary, employmentType, remoteType, applyUrl or expiresAt.

A change of status does not produce job.updated. Status movements have their own events: see job.closed below, and the reopen note under job.created.

A closed job never receives job.updated, whatever changes on it: you were told it closed, and it has no further events unless the employer relists it, which arrives as job.created.

{
  "type": "job.updated",
  "data": {
    "job": { /* full Job object, current values */ },
    "changedFields": ["title", "salaryMax"]
  }
}
data fieldTypeDescription
jobobjectCurrent state of the job (full Job object).
changedFieldsarray of stringNames of fields that changed in this update. Useful for incremental sync.

job.closed

The employer's job board stopped listing the role. A job is closed once it has been absent from the employer's board for 30 days, so this event arrives about 30 days after the role was last listed. The payload is minimal, the same shape as job.deleted, because you already hold the record: look it up by id and mark it closed. If you need the job's other fields, fetch GET /v1/public/jobs/{id}.

Closures are free: they do not count against your monthly job allowance, and they keep arriving even when your account is over its limit, so the jobs you hold do not go stale.

job.closed ignores your subscription's filter, like job.deleted: a job can reach you under one set of attributes and close under another, so every subscription receives every closure. Match it on id, not applyUrl: two different jobs can share an apply URL, and a closure for one must not close the other. If you don't recognise the id, ignore it.

closed means the role is no longer being advertised. It is not used for records we withdraw for our own reasons, such as a listing we could no longer verify; those are removed with job.deleted instead.

A closed job is not necessarily final. If the employer relists it, you will receive job.created for the same id.

{
  "type": "job.closed",
  "data": {
    "job": {
      "id": "01HZX7K4VWJ8MMGS31F7DCYJ8Z",
      "applyUrl": "https://example.com/jobs/123",
      "closedAt": 1715520000
    }
  }
}
data.job fieldTypeDescription
idstringJob ID. Use this to find the record in your store.
applyUrlstringThe job's apply URL. Useful as a secondary key if you stored that.
closedAtinteger (unix seconds)When Jobven closed the job: the same value as closedAt on the job in the Pull API. About 30 days after the role was last listed.

job.deleted

A job was removed from Jobven entirely. Your store should delete the corresponding record. The data.job payload is intentionally minimal: by the time you receive this event, the full record may already be gone from our database.

This event ignores your subscription's filter: you get it for every deletion, including jobs that never matched. See Filter scope. If you don't recognise the id or applyUrl, ignore it.

{
  "type": "job.deleted",
  "data": {
    "job": {
      "id": "01HZX7K4VWJ8MMGS31F7DCYJ8Z",
      "applyUrl": "https://example.com/jobs/123",
      "deletedAt": 1715520000
    }
  }
}
data.job fieldTypeDescription
idstringJob ID. Use this to find and delete from your store.
applyUrlstringThe job's apply URL. Useful as a secondary key if you stored that.
deletedAtinteger (unix seconds)When the deletion occurred.

webhook.test

Fired when you click Send test in the dashboard. Deterministic content: the same fixture every time, regardless of your subscription's filter or event-type selection. Test events:

  • ignore your subscription's events and filter settings
  • don't count against your monthly job allowance
  • are terminal on first failure (no retries)
{
  "type": "webhook.test",
  "data": {
    "job": {
      "id": "job_test_fixture",
      "title": "Senior TypeScript Engineer (Test Event)",
      "companies": [
        { "name": "Jobven Test Co", "website": "https://jobven.example.com" }
      ]
    }
  }
}

Use webhook.test events to wire up and verify your signature handler before live events start landing.