# Status updates back to the ERP

> How the ERP learns what happened to an invoice after Alya Books accepted the message: webhooks or polling, the statuses, rejections, and the XML and PDF to attach to the job.

Source: /docs/guides/erp-status-updates

The reply to a pushed message says whether Alya Books created the invoice. What happens next (submission to the service provider, reporting to the FTA, a rejection) arrives later, usually within minutes. Alya Books checks the provider every five minutes and reports each change by webhook; your side can also poll.

## The statuses

| `invoiceStatus` | Meaning | Webhook event |
|---|---|---|
| `validated` | Created and valid, not yet submitted (the connector's submit setting is off, or approval is pending) | `invoice.created` |
| `submitted` | Handed to the accredited service provider | `invoice.submitted` |
| `delivered` | The buyer's access point accepted it | `invoice.delivered` |
| `accepted` | Reported to the FTA | `invoice.reported` |
| `rejected` | Refused by the provider, the buyer's access point or the FTA | `invoice.rejected` |

`invoice.reported` and `invoice.rejected` are the two your write-back needs; the others are optional progress.

## Webhooks (recommended)

Create a subscription once:

```bash
curl -X POST {BASE}/webhooks -H "X-API-KEY: sk_test_…" -H "Content-Type: application/json" \
  -d '{"targetUrl":"https://your-middleware.example/einvoicing","events":["invoice.reported","invoice.rejected","invoice.submitted"]}'
```

The reply contains the signing secret (`whsec_…`) once. Each delivery is a signed `POST`:

```jsonc
{
  "id": "9b1e…",                          // event id: the same for every subscriber, use it to de-duplicate
  "event": "invoice.rejected",
  "createdAt": "2026-10-13T07:15:00Z",
  "organizationId": "…",
  "data": {
    "id": "ac20fac3-…",                   // invoiceId
    "invoiceNumber": "INV-2027-00412",
    "externalId": "cargowise:DXB:INV:INV-2027-00412",   // match it to the CargoWise transaction
    "invoiceTypeCode": "380",
    "status": "rejected",
    "issueDate": "2026-10-13", "currency": "AED", "total": 17140,
    "buyer": { "id": "…", "name": "…", "trn": "100123456700003" },
    "metadata": { "job": "S00123456", "house_bill": "DXBSHA2701234", "branch": "DXB" },
    "validation": { "validity": true, "errors": [] },
    "rejection": { "stage": "fta", "error": "…", "submissionId": "…", "at": "2026-10-13T07:14:58Z" },
    "isTest": true
  }
}
```

- Match on `data.externalId` (the message key) or `data.invoiceNumber` (the CargoWise number).
- On `invoice.rejected`, `data.rejection.stage` says who refused it (`taxilla` for the service provider, `buyer`, `fta`) and `error` why. Show it to the person who posted the invoice; the fix is a corrected document or a credit note.
- Verify `X-Webhook-Signature` (HMAC-SHA256 of `"<t>.<raw body>"`) and reject timestamps older than five minutes. See [Webhooks](/docs/guides/webhooks#verify-the-signature) for code.
- Answer `2xx` within 10 seconds. Failed deliveries are retried with back-off (checked every five minutes) and stay visible under `GET {BASE}/webhooks/{id}/deliveries`.

## Polling

Without a webhook receiver, poll the invoices still in progress:

```
GET {BASE}/invoices/{invoiceId}/status
```

```jsonc
{
  "invoiceId": "ac20fac3-…",
  "invoiceNumber": "INV-2027-00412",
  "invoiceStatus": "accepted",
  "jobStatus": "done",                    // processing | ready | done | failed
  "authority": [
    { "type": "fta", "provider": "taxilla", "mode": "uat", "status": "accepted", "stage": null,
      "referenceNumber": "90d455ab-…", "requestId": "205a32b2-…", "submittedAt": "…", "completedAt": "…", "error": null }
  ],
  "validation": { "validity": true, "errors": [], "warnings": [] }
}
```

Poll every few minutes, and stop once `jobStatus` is `done` or `failed`. To find invoices by message key, use `GET {BASE}/invoices?externalId=cargowise:DXB:INV:INV-2027-00412`.

## The documents to attach to the job

| Document | Request | Notes |
|---|---|---|
| PINT AE XML as reported | `GET {BASE}/invoices/{id}?format=xml` | After submission this is the exact XML that was sent |
| PDF | `GET {BASE}/invoices/{id}?format=pdf` | The same PDF the app downloads and emails, in the invoice's design |
| A link instead of the file | `GET {BASE}/invoices/{id}/download-link?format=pdf` (or `xml`) | A signed URL that works for 15 minutes without a key, for middleware that fetches files separately |

## Writing back into CargoWise

In the first phase your middleware (or a person) attaches the documents and sets the job's e-invoice status from these events. In the next phase Alya Books posts them into CargoWise itself through eAdaptor's inbound interface; see [What comes later](/docs/guides/cargowise-integration#what-comes-later).
