nango is the only accepted value of RECEIPT_INTEGRATIONS_PROVIDER today.
The three variables that make it exist
503:
Integrations are temporarily unavailable because the connection service is not configured. Contact your Receipt administrator.; running locally they see Integrations are not configured for this local app. Start the integrated stack with bun run start:all, then try again.
The rest of the surface:
The key you cannot have yet
This is a genuine chicken-and-egg, and it is the reason a first deploy looks broken.RECEIPT_INTEGRATIONS_SECRET_KEY is the provider’s own prod environment secret key. The provider only creates real environment keys once its database exists — which happens when you deploy it. So there is no value to configure before the first deploy.
1
Deploy once, without the key
Everything else comes up. Receipt Connect does not work yet; that is expected.
2
Open the provider UI at your public origin
It is served under
/integrations on the public origin, through the gateway.3
Copy the prod environment key
Environment Settings → API Keys. Take the
prod environment secret key in its UUID form — not the public key. Those are two different values and only one of them works.4
Set it and deploy again
Put it in your secrets bundle, upload the bundle, and deploy a second time.
The secret bundle loader validates this before anything is uploaded. A value that is not a UUID v4 is rejected, with a message that names the generated placeholder and the provider’s public key as the two mistakes it is warning against. The three base64 encryption keys in the bundle are checked in the same pass and must each decode to exactly 32 bytes.The reference pipeline also has an explicit bootstrap mode that writes a UUID-shaped placeholder — which is why it passes the loader’s UUID check — so the first deploy can proceed, plus a separate command that replaces the placeholder with the real key afterwards. What actually catches a placeholder is a separate flag: a bundle still carrying the flagged placeholder blocks a production release.
OAuth applications and the callback URL
For each OAuth connector you intend to offer, register an application with that provider, add the callback URL below, and set the connector’sRECEIPT_NANGO_<PREFIX>_CLIENT_ID and _CLIENT_SECRET (with RECEIPT_NANGO_<PREFIX>_SCOPES when the defaults do not fit). OAuth integrations are never synthesized for you.
Because you run the provider yourself, the callback is on your domain — not on the provider’s hosted API:
/oauth subtree to the integration provider, so that path is answered by the provider through your public origin. Every OAuth connector configured with custom credentials — rather than the provider’s own defaults — must have that exact URL in its application record: https, no trailing slash, no port.
Get it wrong and the error comes from the upstream provider, not from Receipt or from the integration provider. Linear, for example, answers:
Webhooks: the primary consistency path
Webhooks are how a completed authorization actually becomes a stored connection. Read repair is the fallback, not the mechanism. Point the provider’s webhook URL at your public origin:RECEIPT_INTEGRATIONS_WEBHOOK_SECRET. The repository ships a script that applies this configuration directly into the provider’s private schema — it sets the webhook URL, clears the secondary URL, enables auth webhooks, disables always-send, turns HMAC on and sets the HMAC key — inside a transaction that rolls back on any failure, and takes exactly one of --apply or --check.
How Receipt verifies one
Receipt computes an HMAC-SHA256 over the raw request body, reads the provider’s signature from thex-nango-hmac-sha256 header, and compares the two with a timing-safe comparison over hex. The route has no other authentication — the signature is the authentication.
An unsigned or wrongly signed request gets:
The two shapes that are handled
Auth creation or override
Auth creation or override
type: "auth", operation: "creation" or "override", success: true.Receipt resolves who and where from the webhook’s tags, with fallbacks:It then upserts the encrypted connection reference.
Auth refresh failure
Auth refresh failure
type: "auth", operation: "refresh", success: false.Receipt marks the matching stored reference invalid, with the reason nango_auth_refresh_failed. Nothing is deleted — the row stays, visibly broken, so a member can reconnect it.When the actor has lost membership
A webhook can arrive after the person who started the authorization has been removed from the workspace. Receipt does not project the connection in that case, and it does not silently keep it either:- The webhook is acknowledged with 200 — so the provider does not retry forever — carrying
{"ok":true,"ignored":true,"reason":"workspace_membership_revoked"}. - The projection is refused, so no reference is stored.
- The connection just created on the provider is best-effort deleted, so no orphaned credential is left behind.
Read repair: the secondary path
Read repair reconciles the provider’s view of connections with Receipt’s stored references. It reads two sources — the provider’s paginated HTTP connection list, and a direct read of the provider’s private connections table filtered on the organization and workspace tags. The second source exists because the HTTP list can omit a provider-only connection, and it is the only thingRECEIPT_INTEGRATIONS_DATABASE_URL is used for.
A stored reference the list did not report is direct-checked against the provider by connection id. A healthy direct result leaves the row alone. Anything else invalidates only that one row, never the rest: reported connection errors give the reason nango_connection_errors, an exhausted refresh gives nango_refresh_exhausted, and a 404 or 410 gives nango_connection_missing.
Where it runs matters:
Read-repair failures are logged and swallowed. That is deliberate: a briefly unreachable provider must never delete a member’s stored connections. A provider outage degrades freshness, not data.
Operational warnings
Receipt pins one specific hosted provider image and applies checked-in patches to it at build time. Each patch is fail-closed: the image build errors out if the pinned upstream shape has drifted, rather than producing an image that is quietly wrong.Two of those patches change what you have to register:
- Azure DevOps is switched from the upstream basic-auth setup (organization URL plus a personal access token) to Microsoft Entra ID OAuth2, the same login Outlook and Azure Blob Storage use — so it needs an Entra application, not a PAT.
- Azure Resource Manager gains a two-step service-principal provider that the pinned image predates. This is the connector the Agent Registry uses to discover agents in Azure subscriptions; the client secret is exchanged by the integration provider server-side and Receipt stores only the non-secret reference.
The free self-hosted provider includes Auth and Proxy but not Functions or the provider’s own MCP server. Receipt therefore builds its ordinary REST coverage on Auth and Proxy alone, so no connector depends on provider-side functions.
3003 and its Connect UI on port 3009, reached through the gateway at /integrations and /integrations-connect. Checking /integrations-connect/ only proves the static UI is being served — check /integrations/health for the provider itself.
Next step: deploy Receipt and learn the guards that stop a bad release.