Skip to main content
Astral uses Stripe webhooks to keep subscription state in sync. When a billing event occurs — a payment succeeds, a subscription is cancelled, or a plan changes — Stripe sends an HTTP POST to the Astral webhook endpoint. Astral verifies the request signature and updates your subscription accordingly. You do not call this endpoint directly; configure it in your Stripe Dashboard and Stripe delivers events automatically.

Stripe webhook endpoint

POST /api/webhooks/stripe
Authentication: Stripe signature verification via the Stripe-Signature header. No bearer token is used. This endpoint verifies the request against your Stripe webhook signing secret before processing any event. Requests that fail signature verification are rejected with 401.

Configuring the webhook in Stripe

1

Open Stripe Dashboard

Go to Stripe Dashboard → Webhooks and click Add endpoint.
2

Set the endpoint URL

Enter the Astral webhook URL provided to you by your account manager.
3

Select events to listen for

Enable the following events:
  • checkout.session.completed
  • customer.subscription.created
  • customer.subscription.updated
  • customer.subscription.deleted
  • invoice.payment_succeeded
  • invoice.payment_failed
4

Save the signing secret

Copy the Signing secret shown after creating the endpoint. You will need this when verifying incoming webhook requests on your end.

Signature verification

Stripe signs every webhook request with a Stripe-Signature header. Astral verifies this signature before processing any event. If you are forwarding or relaying webhook events, always pass the raw bytes of the request body to the verification function — do not parse or re-serialize the JSON first.
Stripe-Signature: t=1714867200,v1=abc123...,v0=def456...

Handled events

checkout.session.completed

Triggered when a Stripe Checkout session completes successfully. Astral activates the corresponding subscription. Event payload shape
{
  "id": "evt_1abc",
  "type": "checkout.session.completed",
  "data": {
    "object": {
      "id": "cs_test_xxx",
      "customer": "cus_abc123",
      "subscription": "sub_xyz789",
      "metadata": {
        "user_id": "usr_athlete1",
        "plan": "pro",
        "billing_interval": "monthly"
      }
    }
  }
}

customer.subscription.updated

Fired when a subscription’s status, plan, or billing details change. Astral maps Stripe’s subscription statuses to the values you see in the /subscriptions/status API response:
Stripe statusAstral status
activeactive
trialingtrial
past_duepast_due
canceledcanceled
unpaidpast_due
pausedsuspended
Event payload shape
{
  "id": "evt_2def",
  "type": "customer.subscription.updated",
  "data": {
    "object": {
      "id": "sub_xyz789",
      "customer": "cus_abc123",
      "status": "active",
      "current_period_start": 1714867200,
      "current_period_end": 1717545600,
      "cancel_at_period_end": false,
      "items": {
        "data": [
          {
            "price": {
              "id": "price_1ProMonthly"
            }
          }
        ]
      }
    }
  }
}

customer.subscription.deleted

Fired when Stripe cancels a subscription. The subscription is marked canceled and historical records are preserved. You keep access through the end of the current billing period. Event payload shape
{
  "id": "evt_3ghi",
  "type": "customer.subscription.deleted",
  "data": {
    "object": {
      "id": "sub_xyz789",
      "customer": "cus_abc123",
      "status": "canceled",
      "current_period_end": 1717545600
    }
  }
}

invoice.payment_succeeded

Fired when an invoice is paid. Astral confirms the subscription renewal and updates the subscription status to active. Event payload shape
{
  "id": "evt_4jkl",
  "type": "invoice.payment_succeeded",
  "data": {
    "object": {
      "id": "in_abc123",
      "customer": "cus_abc123",
      "subscription": "sub_xyz789",
      "billing_reason": "subscription_cycle",
      "amount_paid": 1200,
      "currency": "usd"
    }
  }
}

invoice.payment_failed

Fired when an invoice payment fails. Astral marks the subscription as past_due. The subscription remains active until the end of the grace period — the exact duration depends on your Stripe billing settings. Event payload shape
{
  "id": "evt_5mno",
  "type": "invoice.payment_failed",
  "data": {
    "object": {
      "id": "in_def456",
      "customer": "cus_abc123",
      "subscription": "sub_xyz789",
      "amount_due": 1200,
      "currency": "usd"
    }
  }
}

Response format

On success, the endpoint returns:
{ "received": true }
with HTTP 200 OK.
StatusCondition
400Request body is missing
401Signature verification failed

Testing webhooks

Use the Stripe CLI to forward test events to a local environment:
stripe listen --forward-to localhost:5000/api/webhooks/stripe
Send a test event to verify your configuration:
stripe trigger customer.subscription.updated