Build a custom booking flow

The Zenbooker API provides a logical path to build a complete, end-to-end booking experience within your own application. The process follows a sequence of steps: first, you identify the correct territory for a customer; next, you define the job's scope and duration; then, you find an available time slot; and finally, you create the booking.

This guide walks you through the entire flow with practical examples and code snippets.

Prerequisites

All API requests require an Authorization header with your bearer token:

Authorization: Bearer <YOUR_API_KEY>

Step 1: Identify the service territory

The first and most critical step is determining if you can service a customer's location, and if so, which specific service territory is responsible for it. A territory represents a branch, team of providers, or geographic zone. This identification is essential because all subsequent steps—availability checking, scheduling, and staff assignment—are territory-specific.

Every booking starts with location validation. This step determines:

  • Whether you service the area
  • Which territory (branch/team) handles it

Use the service_area_check endpoint for location lookup and routing:

curl -G "https://api.zenbooker.com/v1/scheduling/service_area_check" \
  --data-urlencode "postal_code=20901" \
  -H "Authorization: Bearer <YOUR_API_KEY>"

Understanding the response:

{
  "in_service_area": true,
  "service_territory": {
    "id": "1586239489452x571449227260704600", // Store this for getting timeslots and booking later
    "name": "Silver Spring",                  // Territory name
    "timezone": "America/New_York"
  },
  "customer_location": {
    "coordinates": {  // Store this to pass to the timeslots endpoint later (optional)
      "lat": 39.0191,
      "lng": -77.0076    
    }
  }
}
}
💡

Why territories matter Territories are geographic boundaries that represent distinct operational units with their own schedules, providers, and rules. The same time might be available in one territory but not another.

Step 2: Define the job and duration

Once you know which territory handles the work, define what the work entails. The goal is determining the job's total duration in minutes, required for finding available time slots.

Option A: Using your Zenbooker service catalog

For customers choosing from existing services and add-ons, first fetch your list of services:

curl "https://api.zenbooker.com/v1/services?is_visible=true" \
  -H "Authorization: Bearer <YOUR_API_KEY>"

The response includes detailed service information:

{
  "results": [
    {
      "service_id": "1760300167598x745029266608947700",
      "name": "Home Theater TV Mounting & Setup",
      "base_duration": 120,
      "base_price": "149.00",
      "sections": [
        {
          "section_id": "1760300168129x855444251360474400",
          "section_type": "price_modifier",
          "title": "TV size (select one)",
          "input_type": "radio_buttons",
          "options": [
            {
              "option_id": "1760300168599x299965067990447740",
              "name": "Up to 49-inch TV",
              "price": "0.00",
              "duration": 0
            },
            {
              "option_id": "1760300168685x597574803337188900",
              "name": "50–65-inch TV",
              "price": "30.00",
              "duration": 15
            }
          ]
        }
      ]
    }
  ]
}

Duration calculation

Calculate total duration by summing:

  • Service base_duration
  • Each selected option's duration

Example: Base service (120 min) + Large TV option (15 min) = 135 minutes total

Option B: Custom services

Choose this for simpler integrations where you define service details dynamically. This approach works well for:

  • Custom service builders: When you've built your own service configuration flow and just need to pass the final price and duration
  • Dynamic pricing models: Services where you calculate price/duration based on external factors Simple bookings: "Book a 1-hour appointment" without options

With this approach:

  • No API call needed in this step
  • Simply determine your duration (e.g., 60 minutes)
  • Set arbitrary service details (name, duration, price) later when creating the job

Step 3: Find available timeslots

Query the schedule for your identified territory using the duration from Step 2:

curl -G "https://api.zenbooker.com/v1/scheduling/timeslots" \
  --data-urlencode "territory=1586239489452x571449227260704600" \
  --data-urlencode "date=2025-10-28" \
  --data-urlencode "duration=135" \
  --data-urlencode "lat=39.0191" \
  --data-urlencode "lng=-77.0076" \
  -H "Authorization: Bearer <YOUR_API_KEY>"

Key parameters

  • territory: Territory ID from Step 1 (required)
  • date: Start date in YYYY-MM-DD format (required)
  • duration: Job duration in minutes (required)
  • days: Number of days to return (default: 7)
  • lat/lng: Customer coordinates for accurate scheduling

Response structure

{
  "territory_id": "1586239489452x571449227260704600",
  "timezone": "America/New_York",
  "days": [
    {
      "date": "2025-10-28",
      "timeslots": [
        {
          "start": "2025-10-28T13:00:00.000Z",
          "end": "2025-10-28T17:00:00.000Z",
          "type": "arrival_window",
          "formatted": "9:00 AM - 1:00 PM",
          "id": "slot_1761051600_1761066000"
        }
      ]
    }
  ]
}

Present these options to customers and capture the selected timeslot id.

Step 4: Create the job

With all necessary information gathered, create the job with a single POST request. The services parameter accepts both catalog services and custom services.

curl -X POST "https://api.zenbooker.com/v1/jobs" \
  -H "Authorization: Bearer <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "territory_id": "1586239489452x571449227260704600",
    "timeslot_id": "slot_1761051600_1761066000",
    "customer": {
      "name": "Jane Doe",
      "email": "[email protected]",
      "phone": "555-123-4567"
    },
    "address": {
      "line1": "123 Main St",
      "city": "Silver Spring",
      "state": "MD",
      "postal_code": "20901"
    },
    "services": [
      {
        "service_id": "1760300167598x745029266608947700",
        "selections": [
          {
            "section_id": "1760300168129x855444251360474400",
            "selected_options": [{
              "option_id": "1760300168685x597574803337188900"
            }]
          }
        ]
      },
      {
        "custom_service": {
          "name": "Haul away old TV",
          "price": 50.00,
          "duration": 15,
          "taxable": false
        }
      }
    ],
    "sms_notifications": true,
    "email_notifications": true
  }'

Service configuration options

Catalog Service

How you represent a customer booking an existing service with selected options.

{
  "service_id": "1760300167598x745029266608947700",
  "selections": [
    {
      "section_id": "1760300168129x855444251360474400",
      "selected_options": [
        {
          "option_id": "1760300168685x597574803337188900",
          "quantity": 1,
          "comments": "Handle with care"
        }
      ]
    }
  ]
}

Custom Service

How you can define custom services for a job with arbitrary price, name, and description.

{
  "custom_service": {
    "name": "Emergency repair consultation",
    "description": "Initial assessment and quote",
    "price": 75.00,
    "duration": 45,
    "taxable": true
  }
}

Additional options

Enhance bookings with optional parameters:

  • assignment_method: "auto" or "offer" for provider assignment
  • sms_notifications: Enable SMS updates for customers
  • email_notifications: Enable email updates
  • job_number: Custom job reference number

Success response

A successful 201 Created response confirms the booking:

{
  "job_id": "1761023232848x682214498988670500",
  "status": "scheduled",
  "customer_id": "1759877773365x180935398640097200",
  "timeslot": {
    "type": "arrival_window",
    "start": "2025-10-28T13:00:00.000Z",
    "end": "2025-10-28T17:00:00.000Z"
  },
  "pricing": {
    "subtotal": "199.00",
    "tax_amount": "15.92",
    "total": "214.92"
  }
}

Best practices

Error handling

  • Always check in_service_area before proceeding
  • Handle timezone conversions properly

Performance optimization

  • Cache service catalog data when appropriate
  • Include customer coordinates for accurate scheduling
  • Batch timeslot requests for multiple days by setting the days parameter.


Your booking flow is complete! The customer is scheduled, and you can display confirmation details with the job ID for future reference.