Handle Adapty subscription events with webhooks

Webhooks let your server receive Adapty subscription events in real time — purchases, renewals, cancellations, billing issues, and refunds — so you can grant access, sync your backend, or trigger workflows. This guide takes you from endpoint to a verified, tested integration on one page, and shows how to have an AI coding agent write the handler for your stack.

Using an AI coding agent? Click Copy for LLM under the title and paste this whole page into your agent — it has the setup, payload, and handler logic it needs.

How Adapty webhooks work

  • One-way and real-time: Adapty sends an HTTP POST to your server when an event occurs — no polling.
  • Two kinds of request: A one-time verification request (sent when you save the integration) and ongoing subscription events.
  • One URL per environment: You configure a separate endpoint for production and for the sandbox.
  • You acknowledge each request: Respond with a 2xx status quickly, and Adapty retries on failure.

Build your endpoint

Create a public HTTPS endpoint that handles two request types:

  • Verification request: Sent once when you save the integration. It has an empty JSON body ({}). Respond with a 2xx status and a JSON body.
  • Subscription events: Ongoing POST requests with the event in the body. Respond 200 within 10 seconds, then do any heavy work asynchronously.

Choose a secret string and store it as an environment variable (for example, ADAPTY_WEBHOOK_SECRET). On every request, verify the Authorization header matches it, and reject the request if it doesn’t — you’ll enter the same secret in the dashboard next.

import express from "express";

const app = express();
app.use(express.json());

const WEBHOOK_SECRET = process.env.ADAPTY_WEBHOOK_SECRET;

app.post("/adapty/webhook", (req, res) => {
  // 1. Verify the shared secret Adapty echoes back.
  if (req.get("Authorization") !== WEBHOOK_SECRET) {
    return res.sendStatus(401);
  }

  // 2. Acknowledge fast, then process asynchronously.
  res.status(200).json({});

  // 3. The verification request has an empty body — nothing to handle.
  const event = req.body;
  if (!event.event_type) return;

  switch (event.event_type) {
    case "subscription_started":
    case "subscription_renewed":
    case "trial_converted":
      // Grant or extend access.
      break;
    case "subscription_expired":
    case "subscription_refunded":
      // Revoke access.
      break;
    default:
      break;
  }
});

app.listen(3000);

Deploy the endpoint to a public HTTPS URL before you configure the integration — Adapty sends the verification request the moment you save.

Key events and the payload

Every event shares the same envelope. The fields vary by event type, store, and the options you enabled. Here is a trimmed subscription_started event:

{
  "profile_id": "00000000-0000-0000-0000-000000000000",
  "customer_user_id": "UserIdInYourSystem",
  "event_type": "subscription_started",
  "event_datetime": "2024-11-15T10:45:36.181000+0000",
  "event_properties": {
    "store": "play_store",
    "currency": "USD",
    "price_usd": 4.99,
    "vendor_product_id": "onemonth_no_trial",
    "transaction_id": "0000000000000000",
    "original_transaction_id": "0000000000000000",
    "subscription_expires_at": "2024-12-15T10:45:36.181000+0000",
    "profile_event_id": "00000000-0000-0000-0000-000000000000"
  },
  "event_api_version": 1
}

The events you’ll handle most often:

Event typeFires when
subscription_startedA user starts a paid subscription
subscription_renewedA subscription renews and bills successfully
subscription_renewal_cancelledA user turns off auto-renew (access lasts until expiry)
subscription_expiredAccess ends after a non-renewed subscription lapses
trial_startedA user starts a free trial
trial_convertedA trial converts to a paid subscription
billing_issue_detectedA renewal payment fails
subscription_refundedA subscription purchase is refunded

For the full event list and every field, see Webhook event types and fields.

Don’t order events by event_datetime — it’s the business time of the event, so events can arrive out of order or share a timestamp. Order by your own receipt time, and deduplicate using profile_event_id or the transaction IDs.

Configure the webhook in Adapty

  1. Open Integrations → Webhook in the Adapty Dashboard.
  2. Turn on the integration.
  3. In Production endpoint URL, enter the HTTPS URL of the endpoint you deployed.
  4. In Authorization header value for production endpoint, enter the same secret your endpoint checks. Adapty sends this value back in the Authorization header on every request. It’s optional but strongly recommended.
  5. To test in the sandbox first, fill in Sandbox endpoint URL and its Authorization header value too.
  6. Click Save. Adapty immediately sends the verification request to your endpoint, which replies with a 2xx to complete setup.

To choose which events to send, map event names, or enable optional fields (trial price, historical events, attribution, user attributes, Play Store token), see Set up webhook integration.

Adapty Dashboard webhook integration settings with the production endpoint URL and Authorization header value fields

Build it with your AI coding agent

Give your AI coding agent this guide and the reference docs as Markdown (add .md to any page URL), tell it your stack, and let it scaffold the handler:

Example prompt:

Read these Adapty webhook docs, then write a webhook handler for my Express app:
verify the Authorization header against ADAPTY_WEBHOOK_SECRET, answer the
verification request, acknowledge events with 200, and grant or revoke access
based on event_type.

The agent writes the handler code, but it can’t deploy your endpoint or configure the dashboard — host the endpoint yourself and set the URL and secret in Integrations → Webhook.

Test your webhook

Test in the sandbox before production:

  1. Set up the sandbox endpoint and secret as described above.
  2. In your sandbox app, make a purchase, start a trial, or issue a refund to trigger an event.
  3. Open the integration’s Last sent events section. A delivered event shows the Success status.

If an event shows Sending failed, your server returned a status outside the 200–399 range — hover over the status for details. For the full testing walkthrough, see Test webhook integration.

Webhook integration Last sent events list showing a delivered event with the Success status

Limits

  • Acknowledge within 10 seconds: If Adapty doesn’t get a response in time, it treats the attempt as failed and retries.
  • Retries: If your status is outside the 200–404 range, Adapty retries with exponential backoff — up to 9 retries over 24 hours.
  • Cancellation delay: Cancellation events can take up to 2 hours to arrive.
  • One URL per environment: To deliver events to several services, point the webhook at your own backend and fan out from there.