Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 4 Nov 2026. What's built · Install 0.2.0
jadpo
Reference: Personal data

Personal 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. 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 97f93be.