Erasure and export
Your data is already in your account, in your own tables and buckets. The export API writes it out as files to your exports bucket, and erasure removes a contact from your tables for good. Both work even when your account is read-only after a subscription ends.
Export your contacts
Section titled “Export your contacts”curl -X POST "$BASE/exports" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"kind":"contacts"}'The export runs in the background. Check on it:
curl "$BASE/exports/<ex_...>" -H "Authorization: Bearer $KEY"When state is ready, the response has a downloadUrl, a presigned link to the file in your own exports bucket, and downloadUrlExpiresAt, when it stops working (at most an hour). Download it:
curl -o contacts.csv "<downloadUrl>"Check it worked: contacts.csv has a header row (contactId, email, status, statusReason, tags, fields, consentSource, consentAt, score, createdAt) and a row for each contact. Export files are deleted from the bucket after 7 days.
Export a campaign’s send records
Section titled “Export a campaign’s send records”recipients gives one row per send record; events gives one row per event (sent, delivered, open, click, bounce, complaint, unsubscribe) for a campaign. Find the campaign’s ID, then export:
curl "$BASE/campaigns?limit=1" -H "Authorization: Bearer $KEY"
curl -X POST "$BASE/exports" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \ -d '{"kind":"recipients","campaignId":"<cm_...>"}'
curl "$BASE/exports/<ex_...>" -H "Authorization: Bearer $KEY"Answer an access request
Section titled “Answer an access request”To give someone a copy of their data (a GDPR access request), look the contact up and export everything about them: the contact record and every send record.
curl -X POST "$BASE/contacts/lookup" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \ -d '{"email":"success+ada@simulator.amazonses.com"}'
curl -X POST "$BASE/exports" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \ -d '{"kind":"contact_data","contactId":"<ct_...>"}'
curl "$BASE/exports/<ex_...>" -H "Authorization: Bearer $KEY"
curl -o contact-data.json "<downloadUrl>"Check it worked: contact-data.json holds the contact and its sendRecords, one for each campaign it was in.
Erase a contact
Section titled “Erase a contact”Erasure deletes the contact, its email index and every one of its send records, in every campaign. We keep only a salted hash of the address, so no import can bring it back.
curl -X DELETE "$BASE/contacts/<ct_...>" -H "Authorization: Bearer $KEY"Check it worked: the delete returns 204, the contact is gone, and the address can’t be added again.
# Expect 404 not_foundcurl "$BASE/contacts/<ct_...>" -H "Authorization: Bearer $KEY"
# Expect 409 contact_suppressedcurl -X POST "$BASE/contacts" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \ -d '{"email":"success+ada@simulator.amazonses.com","consent":{"source":"signup-form","at":"2026-09-01T10:00:00Z","method":"single_opt_in"}}'Two places outside your tables can still hold the address, both in your own account:
- The SES event archive in your
archivebucket keeps full SES events, with addresses, untilArchiveRetentionDaysexpires them. To erase sooner, delete the matching objects, or lower the retention with a stack update. - Exports you made before the erasure, in your
exportsbucket, are deleted after 7 days.
Nothing about the contact is in our account to erase: we never stored it. See Data handling.