Quickstart
Take a sandbox API key through to a confirmed enrolment.
Use sandbox hosts and fictional student data only. Never paste a real key, DDA JWT, or production student record.
What you will do
- Authenticate from your server
- Create a checkout (API)
- Redirect the payer to hosted direct-debit setup (browser)
- Wait until
direct_debit.setup_completeistrue(API) - Confirm the checkout (API)
- Receive a completed enrolment (API)
Setup complete is not Authorised. Mandate active / Authorised can happen later and is not required for confirm. After confirm, Salesforce creates the payment schedule and owns collection. Your integration does not submit payments.
| Kind | Who | Credential |
|---|---|---|
| API | Your backend | Authorization: Bearer <YOUR_API_KEY> |
| Browser | Payer | Setup URL token only — never your API key |
1. Authenticate
StudentPay issues a sandbox key and a provider_code. Examples use SANDBOX_DEMO. Keep the key on your server.
Authorization: Bearer <YOUR_API_KEY>
Content-Type: application/json
2. Check sandbox
API — no key
GET https://sandbox-api.studentpay.co.nz/v1/environment
{
"success": true,
"environment": "sandbox",
"api_base_url": "https://sandbox-api.studentpay.co.nz",
"payment_processor": "gocardless",
"payment_processor_status": "configured",
"readiness": {
"ready_for_api_calls": true
}
}
3. Create a checkout
API — your backend
POST https://sandbox-api.studentpay.co.nz/v1/provider-checkouts
Authorization: Bearer <YOUR_API_KEY>
Content-Type: application/json
Idempotency-Key: DEMO-ENROLMENT-1001
{
"provider": {
"provider_code": "SANDBOX_DEMO",
"provider_order_id": "DEMO-ENROLMENT-1001"
},
"student": {
"first_name": "Alex",
"last_name": "Student",
"email": "alex.student@example.com"
},
"course": {
"course_name": "Example Certificate"
},
"pricing": {
"course_price": 2800,
"amount_to_finance": 2790,
"upfront_payment": 10
},
"plan": {
"payment_type": "interest_free_payment_plan",
"payment_frequency": "Weekly",
"number_of_instalments": 186,
"instalment_amount": 15,
"first_payment_date": "2026-08-20"
}
}
provider.provider_code must match the key.
Create response
{
"success": true,
"idempotent_replay": false,
"provider": {
"provider_code": "SANDBOX_DEMO",
"provider_order_id": "DEMO-ENROLMENT-1001"
},
"records": {
"contact_id": "003XXXXXXXXXXXX",
"opportunity_id": "006XXXXXXXXXXXX",
"dda_id": "a0XXXXXXXXXXXXX"
},
"checkout": {
"checkout_id": "SANDBOX_DEMO-1720000000000-AB12CD34",
"status": "direct_debit_setup_required",
"requires_direct_debit": true
},
"direct_debit": {
"setup_complete": false,
"dda_id": "a0XXXXXXXXXXXXX",
"setup_url": "https://sandbox-api.studentpay.co.nz/api/dd-setup?token=eyJ…",
"token": "eyJ…"
}
}
Keep checkout.checkout_id, records.opportunity_id, direct_debit.dda_id, direct_debit.setup_url, and direct_debit.token (this is checkout.checkout_token on confirm).
setup_complete is false here. That is expected.
4. Redirect the payer to hosted setup
Browser — not your API key
Open direct_debit.setup_url as a redirect or popup. The payer completes BECS NZ bank details on GoCardless. This is not a StudentPay JSON API call.
Do not send Authorization: Bearer <YOUR_API_KEY> from the browser.
5. Return, then wait for setup complete
GoCardless returns the browser to StudentPay. Treat any popup message as a hint only.
API — your backend
GET https://sandbox-api.studentpay.co.nz/v1/provider-checkouts/SANDBOX_DEMO-1720000000000-AB12CD34
Authorization: Bearer <YOUR_API_KEY>
Wait until direct_debit.setup_complete is true:
{
"success": true,
"checkout": {
"checkout_id": "SANDBOX_DEMO-1720000000000-AB12CD34",
"status": "direct_debit_setup_required"
},
"direct_debit": {
"setup_complete": true,
"billing_request_status": "fulfilled",
"mandate_status": "pending_submission",
"authorisation_status": "Customer In Progress",
"authorised": false,
"dda_id": "a0XXXXXXXXXXXXX"
}
}
You may confirm while mandate_status is pending_submission or submitted and authorised is false.
Mandate pending_submission → submitted → active (Authorised) is a separate, asynchronous lifecycle. Do not wait for Authorised before confirm.
6. Confirm enrolment
API — your backend
POST https://sandbox-api.studentpay.co.nz/v1/provider-checkouts/SANDBOX_DEMO-1720000000000-AB12CD34/confirm
Authorization: Bearer <YOUR_API_KEY>
Content-Type: application/json
{
"provider": {
"provider_code": "SANDBOX_DEMO",
"provider_order_id": "DEMO-ENROLMENT-1001"
},
"checkout": {
"checkout_id": "SANDBOX_DEMO-1720000000000-AB12CD34",
"checkout_token": "eyJ…",
"opportunity_id": "006XXXXXXXXXXXX",
"dda_id": "a0XXXXXXXXXXXXX"
},
"payment": {
"payment_method": "studentpay_payment_plan",
"first_payment_date": "2026-08-20",
"deposit_confirmed": true
},
"declarations": {
"payment_plan_accepted": true,
"information_confirmed": true,
"privacy_consent_accepted": true
}
}
Always include {checkoutId} in the path. Use direct_debit.token from create. All three declarations and deposit_confirmed must be true. Confirm does not collect money.
Confirm response
{
"success": true,
"checkout": { "status": "confirmed", "id": "SANDBOX_DEMO-1720000000000-AB12CD34" },
"enrolment": { "status": "complete" },
"direct_debit": {
"setup_complete": true,
"mandate_status": "pending_submission"
},
"agreement": {
"id": "a0JXXXXXXXXXXXX",
"number": "PPA-000001",
"pdf_generated": true
},
"already_confirmed": false
}
A repeat confirm returns already_confirmed: true and does not create a second agreement.
Done
Enrolment is complete for this API when checkout.status is confirmed and enrolment.status is complete.
What happens later, outside this API:
- Mandate may become
active/ Authorised - Salesforce creates the payment schedule
- Salesforce owns collection