Sandbox live

Webhooks

StudentPay → provider outbound webhooks only.

This page does not document inbound GoCardless webhooks.

Configuration

Distinguish code capability from environment configuration.

Layer Current state
Code The NZ API can emit the events listed below, with HMAC signatures and in-request retries
Environment configuration Delivery runs only when StudentPay has set a destination URL and signing secret for that environment
Production Provider webhook destinations are not generally configured. Do not assume live Production delivery

Until a destination is configured, GET /v1/environment may show readiness.webhook_destinations_configured: false. Create, GET, and confirm still succeed. Poll GET for setup complete and agreement ids.

Ask StudentPay to configure sandbox first. Do not treat this page as proof that your Production account already receives webhooks.

Implemented

Only these events are emitted by the NZ API.

Event When Replay
checkout.created New provider checkout created Not sent on idempotent create replay
checkout.confirmed Enrolment confirmed Not sent when already_confirmed
agreement.generated Agreement PDF stored during that confirm Only if pdf_generated is true; not sent on confirm replay

No direct_debit.*, payment, or arrears events are implemented.

Envelope

{
  "event_id": "evt_…",
  "event": "checkout.created",
  "created_at": "2026-08-13T01:40:00.000Z",
  "request_id": "req_…",
  "environment": "sandbox",
  "data": {}
}

Treat event_id as the idempotency key on your side. Consume each event_id once. Correlate with data.provider_order_id and data.checkout_id.

The API retries in-request up to 3 times (0 / 250 / 750 ms) with the same event_id and signed body. Your endpoint should acknowledge with HTTP 2xx when the event is accepted, including replays of the same event_id.

Signature

Header Purpose
X-StudentPay-Event Event name
X-StudentPay-Event-Id evt_…
X-StudentPay-Timestamp Unix seconds used in the signature
X-StudentPay-Signature t=<timestamp>,v1=<hmac_sha256_hex>
X-StudentPay-Delivery-Attempt 1-based attempt
X-StudentPay-Delivery-Max-Attempts Default 3
X-Request-Id Correlated API request id when available

Canonical string: timestamp, then ., then the raw JSON body. HMAC-SHA256 with the configured webhook secret.

checkout.created data

{
  "provider_code": "SANDBOX_DEMO",
  "provider_order_id": "DEMO-ENROLMENT-1001",
  "checkout_id": "SANDBOX_DEMO-1720000000000-AB12CD34",
  "opportunity_id": "006XXXXXXXXXXXX",
  "dda_id": "a0XXXXXXXXXXXXX",
  "contact_id": "003XXXXXXXXXXXX"
}

checkout.confirmed data

{
  "provider_code": "SANDBOX_DEMO",
  "provider_order_id": "DEMO-ENROLMENT-1001",
  "checkout_id": "SANDBOX_DEMO-1720000000000-AB12CD34",
  "opportunity_id": "006XXXXXXXXXXXX",
  "dda_id": "a0XXXXXXXXXXXXX",
  "status": "Confirmed",
  "payment_plan_agreement_id": "a0JXXXXXXXXXXXX"
}

agreement.generated data

{
  "provider_code": "SANDBOX_DEMO",
  "provider_order_id": "DEMO-ENROLMENT-1001",
  "checkout_id": "SANDBOX_DEMO-1720000000000-AB12CD34",
  "opportunity_id": "006XXXXXXXXXXXX",
  "payment_plan_agreement_id": "a0JXXXXXXXXXXXX",
  "payment_plan_agreement_number": "PPA-000001",
  "pdf_content_document_id": "069XXXXXXXXXXXX",
  "agreement_version": "2026-08-02"
}

Not implemented

Do not build handlers for these. They are not emitted.

  • direct_debit.authorised
  • direct_debit.failed
  • direct_debit.setup_completed
  • payment_plan.activated
  • payment.failed
  • payment.confirmed
  • plan.in_arrears
  • plan.cancelled