NextRouteNextRoute

Enrollment API

API reference for the public enrollment flow — form configuration, address validation, promo codes, and lead submission with auto-enroll or manual review.

Enrollment API

The Enrollment API powers the public-facing sign-up form where new customers request service. These endpoints are public (no auth required) and scoped by tenant slug so they can be embedded on any website.

Get Enrollment Configuration

Retrieve branding, service plans, and vertical-specific terminology for a tenant's enrollment form.

curl "https://api.nextroute.app/api/enrollment/config/acme-waste"

Response (200):

{
  "tenant": {
    "name": "Acme Waste Services",
    "slug": "acme-waste",
    "logo_url": "https://cdn.example.com/acme-logo.png",
    "branding": { "primary_color": "#2563EB", "accent_color": "#10B981" }
  },
  "vertical": {
    "name": "Waste Management",
    "slug": "waste",
    "terminology": { "customer": "Resident", "property": "Service Address" },
    "default_plans": []
  },
  "service_plans": [
    {
      "id": "plan_weekly", "name": "Weekly Pickup",
      "price_cents": 3500, "additional_bin_price_cents": 1500,
      "frequency": "weekly", "plan_type": "recurring"
    }
  ],
  "promo_validation_url": "/api/enrollment/validate-promo",
  "trash_day_mode": "per_property",
  "default_trash_day": null
}

Only plans marked as public visibility and recurring type are returned. The trash_day_mode field determines whether the form asks for a trash day per address or uses a tenant-wide default.

Check Service Area

Validate whether an address falls within the tenant's service area before form completion.

curl -X POST "https://api.nextroute.app/api/enrollment/check-address" \
  -H "Content-Type: application/json" \
  -d '{ "tenant_slug": "acme-waste", "address": "123 Main St, Austin, TX 78701" }'

Returns { "serviceable": true } if the address is within a zone, or { "serviceable": false, "message": "..." } if outside. If no zones are configured, all addresses are accepted.

Validate Promo Code

Check a promo code before form submission.

curl -X POST "https://api.nextroute.app/api/enrollment/validate-promo" \
  -H "Content-Type: application/json" \
  -d '{ "tenant_slug": "acme-waste", "promo_code": "SUMMER2026" }'

Valid response: { "valid": true, "discount_type": "percent", "discount_value": 20, "combinable_with_referral": true }

Invalid response: { "valid": false, "error": "Invalid promo code" }

Submit Enrollment

Submit a completed enrollment form. Depending on the tenant's auto_enroll setting, this creates a customer directly or creates an enrollment lead for manual review.

curl -X POST "https://api.nextroute.app/api/enrollment/submit" \
  -H "Content-Type: application/json" \
  -d '{
    "tenant_slug": "acme-waste",
    "first_name": "Sarah",
    "last_name": "Johnson",
    "email": "sarah@example.com",
    "phone": "555-0199",
    "address": "456 Oak Ave, Austin, TX 78702",
    "service_plan_id": "plan_weekly",
    "trash_day": "Tuesday",
    "promo_code": "SUMMER2026",
    "referral_code": "KH7N4WP3",
    "custom_fields_json": { "bin_count": 2, "bin_size": "96gal", "bin_location": "Front of house" }
  }'

Required: tenant_slug, name (or first_name + last_name), email, phone, address, service_plan_id.

Optional: trash_day, trash_pickup_time, promo_code, referral_code, custom_fields_json.

Auto-Enroll Response (201)

When auto_enroll is enabled, a customer, property, Stripe customer, and SetupIntent are created immediately. The property is auto-matched to a service zone and synced to the appropriate route template.

{
  "success": true, "type": "customer", "id": "cus_new123",
  "setup_client_secret": "seti_abc_secret_xyz",
  "stripe_account_id": "acct_stripe456"
}

Lifecycle hooks run automatically: welcome email, route template sync, referral processing, and enrollment confirmation.

Manual Review Response (201)

When auto_enroll is disabled, an enrollment lead is created for the operator to review.

{
  "success": true, "type": "lead", "id": "lead_new456",
  "setup_client_secret": "seti_def_secret_uvw",
  "stripe_account_id": "acct_stripe456"
}

Payment Collection

Both responses include setup_client_secret and stripe_account_id when the tenant has Stripe Connect configured. Use these with Stripe.js to collect the payment method via a SetupIntent on the tenant's connected account.

Duplicate Prevention

The endpoint rejects submissions when an active customer with the same email exists, an active property with the same address exists, or the address is outside all configured service zones (all return 400).

Promo + Referral Stacking

If a promo code's combinable_with_referral flag is false, the referral code is silently dropped. When true, both discounts apply.