Send emails and transactions via the Adapty Mail API

The Adapty Mail API lets you send user profiles and transactions to Adapty Mail directly from your server, without routing data through the Adapty SDK. Use it when you want to:

  • Add subscribers when you don’t have a base in Adapty Mail yet.
  • Reuse the subscriber base from your other apps.
  • Feed Adapty Mail server-to-server, with your backend as the source of truth.
Note

API or SDK? Most apps send data to Adapty Mail through the Adapty SDK, which collects emails and purchases automatically — see Connect Adapty to Adapty Mail. Choose the API when your app has no Adapty SDK, when the data already lives on your server, or when you import subscribers from another source. You can also use the API alongside another entry point. Adapty Mail matches people by email address. Two sources reporting the same address land on one profile; two different addresses create separate profiles, even when customer_user_id is identical.

Before you start

Warning

Finish setting up Adapty Mail before you send data — that means a campaign, segments (if you need them), a web paywall, and a launched flow. Adapty Mail sends emails only to profiles created after this setup is complete; profiles you send earlier won’t receive any emails. Follow Get started with Adapty Mail first, then return here.

You also need your API key and base URL:

  • Secret API key: In Adapty Mail, go to Settings > Project and copy the Secret key. The key is project-specific, so the API knows which project the data belongs to.
  • Base URL: All requests go to https://api-mail.adapty.io.
  • Authentication: Send the key in the Authorization header as Bearer {your_secret_api_key}.
Important

Get explicit consent before you collect emails and send them to Adapty Mail. You are responsible for compliance with GDPR, CAN-SPAM, and similar regulations in your markets.

Send user profiles

A profile carries the user’s email and attributes. To create or update one, send a POST request to /api/v1/profile/save/.

Three fields are required:

  • A stable external_profile_id that your app or backend owns
  • The email Adapty Mail delivers campaigns to
  • external_created_at — the user creation time, which you can use in segments
Important

Always send a stable external_profile_id, never an anonymous or per-install value. Adapty Mail uses it to link emails, clicks, and purchases to one profile.

If the same person also reaches Adapty Mail through another entry point, send customer_user_id — the same user ID that source sends. For the Adapty SDK that’s the value you pass to Adapty.identify(); FunnelFox sends the one it holds. It doesn’t decide which profile they land on; the email address does that. What it adds is a second way to find the profile: a transaction event carrying it resolves without an email, and a deletion can name it instead of the address.

curl --request POST \
  --url 'https://api-mail.adapty.io/api/v1/profile/save/' \
  --header 'Authorization: Bearer {your_secret_api_key}' \
  --header 'Content-Type: application/json' \
  --data '{
    "external_profile_id": "user_12345",
    "external_created_at": "2026-06-01T10:30:00Z",
    "email": "jane@example.com",
    "country": "US",
    "custom_attributes": {
      "plan": "trial"
    }
  }'
Note

country and store_country take a two-letter ISO 3166-1 alpha-2 code — US, not USA or United States. Any other value is rejected.

device_info describes the user’s device. The object is optional, but platform must be present whenever you send it.

FieldOptionalSegment filterNotes
platformNoYes
deviceYesYesThe device model.
osYesYes
localeYesYes
app_versionYesYesCompared in version order, so 1.10.0 ranks above 1.9.0.
timezoneYesNoSets when the user receives email. Without it, Adapty Mail treats them as UTC, so the 08:00–21:00 send window follows UTC rather than their own day.

See the Save profile reference for every available field.

Send transaction events

Note

A profile with an email is enough to reach users in the never purchased flow. Users in every other flow also need transaction events.

Every flow except never purchased is driven by purchase history. Send a profile’s transaction events as you handle purchases, renewals, and cancellations, so Adapty Mail can place it in the right flow. Transaction events also power revenue attribution. Skip them only if you run never purchased campaigns exclusively.

To record a transaction, send a POST request to /api/v1/profile/transaction-event/save/. Use the same external_profile_id you sent with the profile so Adapty Mail links the transaction to the right user.

curl --request POST \
  --url 'https://api-mail.adapty.io/api/v1/profile/transaction-event/save/' \
  --header 'Authorization: Bearer {your_secret_api_key}' \
  --header 'Content-Type: application/json' \
  --data '{
    "event_type": "subscription_started",
    "event_id": "evt_abc123",
    "event_datetime": "2026-06-10T14:20:05Z",
    "external_profile_id": "user_12345",
    "store": "app_store",
    "store_product_id": "premium_monthly",
    "store_transaction_id": "1000000123456789",
    "store_original_transaction_id": "1000000123456789",
    "purchased_at": "2026-06-10T14:20:00Z",
    "originally_purchased_at": "2026-06-10T14:20:00Z",
    "price_usd": "9.99"
  }'

See the Save transaction event reference for every available field.

You can send a transaction before the profile exists. Adapty Mail keeps the event unattached, then links it to the profile on the next save that includes the same external_profile_id.

Delete a profile

To fulfil a right-to-erasure request, send the profile’s identifiers to /api/v1/profile/delete/. Adapty Mail erases the profile’s personal data and cancels its scheduled emails.

curl --request POST \
  --url 'https://api-mail.adapty.io/api/v1/profile/delete/' \
  --header 'Authorization: Bearer {your_secret_api_key}' \
  --header 'Content-Type: application/json' \
  --data '{
    "external_profile_id": "user_12345"
  }'

Identify the profile by external_profile_id, customer_user_id, or email — send at least one. Adapty Mail clears the stored data, but it doesn’t block the address: if a source sends it again, a new profile appears with no history. To keep someone out for good, stop the source from sending their data.

A delete request that times out leaves you unsure whether it landed, so you send it again. That second call is safe when you identify the profile by external_profile_id or customer_user_id — it succeeds and changes nothing. A second call carrying only email returns 404, because the first deletion cleared the address it would have matched. The Delete profile reference lists the response codes.

Map your events to flows

Send the event_type that matches what happened. Adapty Mail derives the profile’s state from its event history and routes it to the matching flow.

event_typeSend it whenFlow
subscription_startedA user starts a new subscription.Active — no re-engagement flow
subscription_renewedA subscription auto-renews.Active — no re-engagement flow
subscription_renewal_reactivatedA user turns auto-renew back on.Active — no re-engagement flow
non_subscription_purchaseA user makes a one-time purchase.Active — no re-engagement flow
subscription_renewal_cancelledA user turns off auto-renew (still active until expiry).Renewal cancelled
billing_issue_detectedA renewal payment fails.Billing issue
entered_grace_periodPayment fails but the user is still in a grace period.Billing issue
subscription_expiredA subscription lapses and access ends.Expired
subscription_refundedA subscription purchase is refunded.Refunded
non_subscription_purchase_refundedA one-time purchase is refunded.Refunded