Webhooks

Configure HTTPS endpoints under Developer → Webhooks in the portal. Subscriptions belong to an organization and mode (Live or Sandbox), and receive selected events for taxpayers that granted it access. Delivery is at least once and may be out of order.

Taxpayer connection events

Onboarding and OAuth consent are separate. The taxpayer first selects Wafeq FZ LLC (Wafeq e-invoicing ASP) in EmaraTax, completes business verification and registration, then approves your app's requested permissions. Only that grant gives your organization access. See Connect a taxpayer.

Subscribe before connecting your first taxpayer:

EventMeaning for your organization
taxpayer.createdYour organization gained its first active grant on the taxpayer. It may have onboarded earlier: this reports a connection, not a new global taxpayer record.
taxpayer.updatedThe taxpayer's details or onboarding status changed, or your active grants changed. Adding a grant, or revoking one while another remains, updates the connection. Inspect data.grants, data.status and top-level previous_attributes.
taxpayer.deletedYour organization lost its last active grant, whether the taxpayer revoked access or your organization disconnected. Stop acting for the taxpayer. This does not mean its record was deleted or it was offboarded from Wafeq e-invoicing ASP.

There are no separate taxpayer.onboarded, taxpayer.offboarded or grant.revoked topics. While your organization still has access, onboarding, reverification and deregistration changes arrive through taxpayer.updated and its status fields. Onboarding alone sends no taxpayer.created to an organization without a grant. Loss of ASP registration and revocation of your app's grant are different changes.

Correlate events using data.taxpayer_id and grant IDs in data.grants. After updates or access loss, reconcile current connections with GET /grants using organization credentials. Do not restore access from a late taxpayer.created delivery. Revoked grant tokens stop working immediately.

Document events

A connected organization receives subscribed document events only where its active grants cover the document and required scopes. Consent does not replay historical events: use the document list APIs for history.

Supported document topics are document.sent, document.received, document.status and document.reported. A sent event records the send result; it does not establish recipient acceptance. Reporting events include REPORTED, NOT_COMPLIANT and FAILED outcomes.

New reporting events also include document_id, submission_id, document_type_code, supersedes_id and submission_message_id where known. Use GET /invoices/{document_id}/reports for each filing and its outcome. An R filing repairs the same AP document; transport retries do not create another document attempt. Correlate by AP document ID, not only source_id, and retain late events against their original attempt. Older events may omit these fields.

Event envelope

The JSON envelope contains id, type, version, created_at, participant_id, environment, test and data. Document correlation fields include document_id, message_id and source_id where available. Synthetic tests have test: true and do not submit an invoice.

Taxpayer updates include previous_attributes with only changed attributes and their old values, including changed metadata keys. A newly added metadata key has a null previous value. No-op writes do not emit updates.

Verify signatures

Developer webhooks use Standard Webhooks headers: webhook-id, webhook-timestamp and webhook-signature. Before processing, verify the raw request body with the Standard Webhooks SDK and your endpoint's signing secret. The signature covers the event ID, delivery timestamp and body. Keep timestamp checks enabled to reject replays.

from standardwebhooks.webhooks import Webhook

verified_event = Webhook(signing_secret).verify(raw_body, request_headers)

Acknowledge and retry safely

Deduplicate on webhook-id, also the envelope id, and return a 2xx response after durable acceptance. Failed deliveries are retried with exponential backoff. The portal shows delivery attempts and supports manual retries.

Signing-secret rotation has a 24-hour overlap. Copy the new secret after rotating, and update your receiver during that window. Configure Live and Sandbox webhook endpoints separately.


Did this page help you?