Worksome · AsyncAPI Specification

Worksome Webhooks

Version

View Spec View on GitHub CompanyFreelancer Management SystemContingent WorkforceHuman ResourcesStaffingWorkforce CompliancePaymentsGraphQLWebhooksDenmarkAsyncAPIEvents

AsyncAPI Specification

worksome-webhooks.yml Raw ↑
generated: '2026-09-04'
method: searched
source: https://docs.worksome.com/webhooks/ + https://docs.worksome.com/webhooks/reference/ + https://docs.worksome.com/webhooks/guides/introduction/ + https://docs.worksome.com/webhooks/guides/handle-webhooks/ + the eight per-event reference pages
note: >-
  Worksome publishes a documented, per-event webhook catalogue with full payload examples but no
  AsyncAPI document — /asyncapi.yaml and /asyncapi.json are not served on any host, and the
  GitHub organisation publishes none. This artifact captures the real event surface as
  documented; NO AsyncAPI spec has been generated, because generating one would assert a machine
  contract Worksome does not publish. Type Webhooks is wired in apis.yml; type AsyncAPI is not.
asyncapi_published: false
asyncapi_probed:
- {url: 'https://api.worksome.com/asyncapi.json', status: 404}
- {url: 'https://api.worksome.com/openapi.json', status: 404}

transport:
  protocol: HTTPS
  method: POST
  content_type: application/json
  direction: outbound (Worksome to consumer)
  subscription_management: >-
    Endpoint URL and shared secret are agreed with Worksome. Subscriptions can also be managed
    programmatically — createWebhook, updateWebhook, deleteWebhook mutations and webhooks /
    webhook queries, or worksome webhooks create|list|delete from the CLI.

security:
  signature_algorithm: HMAC-SHA256
  signature_header: Signature
  signed_payload: the raw request body, before parsing
  secret: shared secret agreed with Worksome out of band
  verification_guidance: Constant-time comparison (hash_equals / crypto.timingSafeEqual / hmac.compare_digest); published in PHP, JavaScript and Python.
  timestamp_in_signature: false
  replay_protection: >-
    None in the signature itself — the header carries a bare hex digest with no timestamp and no
    version prefix, so a captured request stays valid indefinitely. The docs compensate by
    requiring idempotent handlers and duplicate detection on entity ids.
  ip_allowlisting: Recommended by the docs ("allowlist Worksome's IP ranges"), but no IP ranges are published.
  secret_rotation: Manual, coordinated with Worksome support.

delivery:
  expected_response: any 2XX
  unknown_events: Docs instruct returning 200 even for unrecognised event types, so new events can be added without breaking consumers.
  timeout_seconds: 60
  retry_attempts: 5
  retry_schedule: [10s, 100s, 1000s, 10000s]
  retry_schedule_note: Exponential; total window is roughly 3.1 hours from first failure to final attempt.
  after_exhaustion: Delivery stops. Every attempt is logged and Worksome can restart sending for a specific event on request to customer service.
  manual_replay: retryWebhookEvent mutation
  delivery_logs: webhookEvents and webhookEventLogs queries
  ordering_guarantee: none stated
  at_least_once: true

payload_philosophy: >-
  Deliberately minimal. Payloads carry identifiers and a small set of key fields, not full
  objects; the documented pattern is to receive the event and then call the GraphQL API with the
  ids for anything more. The ids in a webhook payload are the same Global IDs the API uses.

event_count: 17
subscribable_but_never_delivered: 1

events:
- identifier: contractAccepted
  name: Contract Accepted
  group: hires_and_contracts
  trigger: A worker accepts a contract.
  payload_keys: [contract, worker, trustedContact, customFieldValues]
  docs: https://docs.worksome.com/webhooks/reference/events/contract-accepted/
- identifier: hireAccepted
  name: Hire Accepted
  group: hires_and_contracts
  trigger: Never delivered.
  delivered: false
  warning: >-
    Subscribable but dead. The value exists in the subscription list and nothing in the platform
    emits it — when a worker accepts, the hire-accepted handler sends contractAccepted instead.
    Worksome documents this explicitly. Subscribe to contractAccepted for that moment.
- identifier: hireUpdated
  name: Hire Updated
  group: hires_and_contracts
  trigger: A hire or one of its related objects changes.
  payload_keys: [contract, worker, trustedContact, customFieldValues]
- identifier: hireCancelled
  name: Hire Cancelled
  group: hires_and_contracts
  trigger: A hire is cancelled before it became active.
  payload_keys: [contract, worker, trustedContact, customFieldValues, cancelReason]
- identifier: hireEnded
  name: Hire Ended
  group: hires_and_contracts
  trigger: A hire reaches its natural end.
  payload_keys: [contract, worker, trustedContact, customFieldValues]
- identifier: hireTerminated
  name: Hire Terminated
  group: hires_and_contracts
  trigger: A hire is ended early with a termination reason.
  payload_keys: [contract, worker, trustedContact, customFieldValues, terminatedReason]
  enum_note: >-
    terminatedReason is one of worker_unavailability, project_completed_early,
    mutual_agreement_to_terminate, budget_constraints, change_in_project_scope,
    performance_issues, communication_issues, personal_reasons, legal_or_compliance_issues,
    violation_of_contract_terms, unforeseen_circumstances, dissatisfaction_with_quality_of_work,
    conflict_of_interest, other.
- identifier: trustedContactUpdated
  name: Trusted Contact Updated
  group: talent_pool
  trigger: A trusted contact changes — contact details, skills, and so on.
  payload_keys: [trustedContact, worker]
  caveat: The payload explicitly does NOT say what changed. The consumer must re-query the API to diff.
- identifier: paymentRequestIssued
  name: Payment Request Issued
  group: payment_requests
  trigger: A worker submits a payment request.
  payload_keys: [paymentRequest, worker]
- identifier: paymentRequestApproved
  name: Payment Request Approved
  group: payment_requests
  trigger: The company approves it, or auto-approval applies.
  payload_keys: [paymentRequest, worker]
- identifier: paymentRequestRejected
  name: Payment Request Rejected
  group: payment_requests
  trigger: The company rejects it.
  payload_keys: [paymentRequest, worker]
- identifier: paymentRequestPaid
  name: Payment Request Paid
  group: payment_requests
  trigger: The company pays it.
  payload_keys: [paymentRequest, worker]
- identifier: paymentRequestCancelled
  name: Payment Request Cancelled
  group: payment_requests
  trigger: It is cancelled.
  payload_keys: [paymentRequest, worker]
- identifier: paymentRequestWorkerPaidOut
  name: Payment Request Worker Paid Out
  group: payment_requests
  trigger: The worker has been paid out.
  payload_keys: [paymentRequest, worker]
- identifier: paymentRequestRecruiterPaidOut
  name: Payment Request Recruiter Paid Out
  group: payment_requests
  trigger: The staffing agency has been paid out.
  payload_keys: [paymentRequest, worker]
- identifier: invoiceCreated
  name: Invoice Created
  group: invoicing
  trigger: An invoice is created.
  payload_keys: [invoice]
- identifier: invoicePaid
  name: Invoice Paid
  group: invoicing
  trigger: An invoice is paid.
  payload_keys: [invoice]
- identifier: creditNoteCreated
  name: Credit Note Created
  group: invoicing
  trigger: A credit note is created.
  payload_keys: [creditNote]

shared_payload_shapes:
  payment_requests: All seven payment-request events carry an identical payload shape; only the event field differs, so one handler can serve all seven.
  invoicing: invoiceCreated and invoicePaid wrap under `invoice`; creditNoteCreated wraps the same object shape under `creditNote`.

hire_status:
  field: data.contract.hireStatus
  added: '2025-06-03'
  present_on: [contractAccepted, hireCancelled, hireEnded, hireTerminated, hireUpdated]
  values: [draft, offered, ready, active, ended, cancelled, terminated]
  case_note: >-
    Webhook payloads carry these lowercase; the GraphQL HireActiveStatus enum exposes the same
    values UPPERCASE. The docs instruct comparing case-insensitively when matching webhook
    payloads against API responses.
  guidance: >-
    Read hireStatus rather than inferring state from which event arrived. contractAccepted,
    hireUpdated and hireEnded can fire around the same moment because accepting a contract also
    changes the hire.

stable_reference:
  field: data.contract.hireId
  rationale: >-
    Revising contract terms creates a new contract with a new id while hireId stays constant.
    Integrations should key on hireId to keep continuity across contract revisions.

consumer_requirements:
  idempotent_handlers: required
  idempotency_key: entity ids in the payload
  rationale: At-least-once delivery with up to 5 retries, plus overlapping hire lifecycle events, means duplicates are expected rather than exceptional.

Work with this as data

Every AsyncAPI spec here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for asyncapi

4 MCP tools reach this
  • find_asyncapisBrowse and filter every AsyncAPI spec in the catalog.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This AsyncAPI spec
curl "https://apis.io/api/v1/asyncapis/worksome-webhooks"
All asyncapi
curl "https://apis.io/api/v1/asyncapis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no email required.

A second provider on the same verified email joins the account you already have.