Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 5 Nov 2026. What's built · Install 0.3.0
jadpo
ReferencePersonal data

DataPersonal data

Erasure at providers

When your app sends a person's data to another company, such as a payment provider, erasing the person also asks that provider to erase them. Two declarations make that work. The provider's package declares, in its provider.jadpo, which call erases a person and what it takes. Your app's connection names the field that holds the person's key at that provider:

packages/stripe/v2024_06_20/provider.jadpo
// packages/stripe/v2024_06_20/provider.jadpoprovider {    ...    erasure: {        call: customers.delete        by: CustomerId    }}// app.jadpostripe: {    package: Stripe.v2024_06_20    domains: [payments]    credential: config.stripe_secret_key    erasure: {        key: User.stripe_customer_id    }}

Erasure emits a PersonErased event, which holds the person entity's name, the person's id and when they were erased, and nothing personal, besides the keys below. The erasure: is a declaration on the package, not part of any capability, so Stripe and Postmark are erased the same way.

  • Your app first, then the providers. When User.erase runs, Jadpo copies each connection's key, here stripe_customer_id, into the erasure event. It then erases the person in one transaction, as above. After that commits, a generated subscriber calls each provider's erasure operation, retried until the provider confirms. So a provider never holds the only record of someone your app still has.

  • The completion email waits for every provider. The erasure-completed email, one of the built-in emails, goes only once every provider has confirmed. A delivery that gives up is parked for an operator and shown in the privacy request's state and in jadpo privacy requests, which isn't built yet. It is never left quietly incomplete.

  • A copied key that is itself personal data, such as an email address used as the provider's key, stays only in the outbox until it is delivered. The personal-data report lists it there.

  • A connection that receives personal data but can't erase it is a compile error. That is a connection whose package declares no erasure:, or which names no key, or reached with a driver rather than a package; or one whose key is on one person entity while it also receives the data of another. An acceptance, a committed file in which a person states why the data stays there (tax records at an accounting provider, say), allows it, and the report lists the connection and the reason:

    examplefragment
    /// Acme Books must keep invoices for seven years for tax.acceptance {    changes: {        accounting: ErasureNotPossible.data_stays    }}

    It holds for as long as the connection keeps data, so a project that already sends some needs it as soon as a change is judged, and later changes need no other. Taking it away takes away what it allowed. It covers the connection: a new field sent there later needs no other acceptance, and shows in the approval review's difference of the report. A store of files on the server's own disk is no other company, so it needs none: a file goes with the records that hold it.

  • The declarations are checked, since each lets a connection off that acceptance. call: is an operation of the package that changes something and takes only the key: one field of its input of the type by: names, any other optional (package.provider_invalid). key: is a field of a person: true entity, of that type (connection.value_invalid).

  • Anything else your app sends data to, such as a CRM it calls through its own integration, can be erased by your own PersonErased subscribers.

Markdown for agents: /docs/erasure-at-providers.md.Built from Jadpo main at aee2e8f.