---
title: "Receive invoices"
description: "Receive invoices from Peppol: subscribe a webhook, verify the HMAC signature, fetch the structured view and advance the lifecycle — plus the polling fallback and event replay."
canonical: "https://docs.get-flowie.com/guides/receive-invoices"
source: "https://docs.get-flowie.com/guides/receive-invoices.html"
---

# Receive invoices

Guides

# Receive invoices

Inbound documents arrive as `document.received` webhooks. Webhooks are the recommended path; polling is the fallback when you cannot expose an HTTPS endpoint.

## 1 · Subscribe once
[code] 
    curl -X POST …/v1/webhooks \
      -H "Authorization: Bearer $FLOWIE_KEY" \
      -d '{
        "url":"https://example.com/hooks/peppol",
        "events":["document.received","document.updated","lifecycle.updated"]
      }'
[/code]

The full event catalogue is in the [webhook reference](<../reference/webhooks.html#events>).

## 2 · Verify the HMAC on delivery

Every delivery carries `X-Flowie-Signature: t=<unix>,v1=<sha256-hex>` over `t + "." + raw_body`. Compare in constant time, against the _raw_ body — a re-serialised JSON body will not match — and reject anything older than five minutes. See [signing & verification](<../reference/webhooks.html#signing>).

## 3 · Fetch the structured view
[code] 
    curl …/v1/documents/{id}/structured \
      -H "Authorization: Bearer $FLOWIE_KEY"
[/code]

A flat, agent-friendly projection of the document — push it into your ERP, AP automation or warehouse. You can also pull the canonical [UBL XML](<../reference/index.html#document-xml>) or a [PDF rendering](<../reference/index.html#document-pdf>).

## 4 · Move the lifecycle along

Call [`POST /v1/documents/{id}/lifecycle`](<../reference/index.html#update-lifecycle>) as the invoice is reviewed, approved, disputed and paid. We report the transitions to the local regime (France PPF, Italy SDI) for you. On the French side, mind the difference between [refusal (210) and technical rejection (213)](<../compliance/fr/refusal-rejection.html>) — one is terminal.

## Polling instead of webhooks

No public endpoint? Poll [`GET /v1/documents`](<../reference/index.html#list-documents>) with `direction=incoming`. It is cursor-paginated: keep passing the returned `cursor` until `hasMore` is `false`, and never hard-code an offset.
[code] 
    curl "…/v1/documents?direction=incoming&limit=100" \
      -H "Authorization: Bearer $FLOWIE_KEY"
[/code]

## If you miss an event

Failed deliveries retry eight times over roughly 20 hours, then the webhook auto-pauses. You can replay any single event with [`POST /v1/events/{id}/replay`](<../reference/index.html#replay-event>), or acknowledge a backlog with [`POST /v1/events/ack`](<../reference/index.html#ack-batch>). The [retry schedule](<../reference/webhooks.html#retries>) is in the webhook reference.

## Next

  * [Send an invoice](<send-invoice.html>) — the outbound half.
  * [Webhook cookbook](<../reference/webhooks.html>) — events, payloads, signing, retries.
  * [Webhook fixtures](<../fixtures/>) — signed sample payloads to develop against.
