Custom Booking Fields

Extend appointments with custom data fields to capture intake information, preferences, or integration-specific metadata.

Environment tip: All examples use v3.onsched.com for production. Replace the host with api-stage.onsched.com when calling the staging environment.

What Are Custom Fields?

Custom Fields allow you to attach additional data to appointments beyond the standard properties (customer, time, service, resources). Store information like:

  • Intake forms – Health conditions, preferences, special requests
  • Business data – Order numbers, project IDs, reference codes
  • Integration metadata – External system IDs, sync tokens
  • Custom questions – Any information relevant to your workflow

Custom Fields are stored as key-value pairs and returned with appointment data.

How Custom Fields Work

Data Structure

Custom Fields are stored in an HSTORE format (PostgreSQL key-value store):

{
  "CustomFields": {
    "dietaryRestrictions": "vegan",
    "parking": "required",
    "referralSource": "google",
    "notes": "prefers morning appointments"
  }
}
  • Keys – Field names (strings)
  • Values – Field values (strings only)

All values are strings—store numbers, booleans, or dates as strings and parse in your application.

Where Custom Fields Appear

Custom Fields can be attached to:

  • Appointments – Most common use case
  • Customers – Customer profile data
  • Services – Service metadata
  • Resources – Resource attributes
  • Locations – Location-specific settings

This guide focuses on appointment custom fields.

Creating Appointments with Custom Fields

Basic Example

curl -X POST "https://v3.onsched.com/v3/appointment?\
LocationId=LOCATION_ID&\
ServiceId=SERVICE_ID" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "Customer": {
      "firstName": "Alex",
      "lastName": "Chen",
      "email": "[email protected]"
    },
    "Unavailability": {
      "startTime": "2025-12-01T10:00:00Z",
      "endTime": "2025-12-01T10:30:00Z"
    },
    "duration": 30,
    "CustomFields": {
      "howDidYouHear": "friend referral",
      "specialRequests": "needs wheelchair access",
      "confirmationPreference": "sms"
    }
  }'

Response includes CustomFields:

{
  "id": "appointment-uuid",
  "status": "BK",
  "Customer": {...},
  "CustomFields": {
    "howDidYouHear": "friend referral",
    "specialRequests": "needs wheelchair access",
    "confirmationPreference": "sms"
  },
  ...
}

Intake Form Example

For appointments requiring pre-booking questions:

{
  "CustomFields": {
    "hasAllergies": "true",
    "allergyDetails": "peanuts, shellfish",
    "medications": "none",
    "emergencyContactName": "Jamie Chen",
    "emergencyContactPhone": "+15550100",
    "insuranceProvider": "BlueCross",
    "insuranceId": "BC123456789"
  }
}

Build intake forms in your UI and pass responses as CustomFields.

Integration Metadata Example

For syncing with external systems:

{
  "CustomFields": {
    "crmContactId": "salesforce-12345",
    "externalOrderId": "ORDER-2025-001",
    "syncToken": "abc123def456",
    "importedFrom": "legacy-system",
    "legacyAppointmentId": "old-db-999"
  }
}

Use CustomFields to maintain references to external databases.

Updating Custom Fields

Replacing All Custom Fields

Use PUT /v3/appointment/:id to replace CustomFields entirely:

curl -X PUT https://v3.onsched.com/v3/appointment/APPOINTMENT_ID \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "CustomFields": {
      "arrived": "true",
      "arrivalTime": "2025-12-01T09:55:00Z",
      "checkedInBy": "receptionist-jane"
    }
  }'

This replaces any existing CustomFields with the new object.

Partial Updates

To add or modify specific fields without losing existing ones:

  1. Retrieve current appointment with GET /v3/appointment/:id
  2. Merge new fields into existing CustomFields
  3. Send full CustomFields object via PUT

Example:

# Step 1: Get current appointment
current_fields=$(curl https://v3.onsched.com/v3/appointment/APPOINTMENT_ID \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" | jq '.CustomFields')

# Step 2: Merge with new fields (application logic)
# Add: "checkedIn": "true"

# Step 3: Update with merged fields
curl -X PUT https://v3.onsched.com/v3/appointment/APPOINTMENT_ID \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "CustomFields": {
      ...existing_fields,
      "checkedIn": "true"
    }
  }'

Retrieving Custom Fields

Custom Fields are included in all appointment responses:

# Get single appointment
curl https://v3.onsched.com/v3/appointment/APPOINTMENT_ID \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

# List appointments (includes CustomFields for each)
curl https://v3.onsched.com/v3/appointments?limit=20 \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response:

{
  "id": "appointment-uuid",
  "status": "BK",
  "CustomFields": {
    "field1": "value1",
    "field2": "value2"
  },
  ...
}

Empty CustomFields return as {} (empty object), never null.

Use Cases

Healthcare Intake

Capture patient information before appointments:

{
  "CustomFields": {
    "reasonForVisit": "annual checkup",
    "symptoms": "none",
    "hasInsurance": "true",
    "primaryPhysician": "Dr. Martinez",
    "consentFormSigned": "true"
  }
}

Event Registration

For workshops, classes, or events:

{
  "CustomFields": {
    "companyName": "Acme Corp",
    "jobTitle": "Marketing Manager",
    "attendeeCount": "1",
    "dietaryRestrictions": "vegetarian",
    "shirtSize": "medium"
  }
}

Service Customization

For customizable services:

{
  "CustomFields": {
    "preferredStylist": "Jane",
    "serviceAdd-ons": "deep conditioning,scalp massage",
    "productPreference": "organic-only",
    "colorFormula": "mix-45-30-25"
  }
}

Delivery or On-Site Services

For mobile or home services:

{
  "CustomFields": {
    "serviceAddress": "123 Main St, Apt 4B",
    "parkingInstructions": "use visitor spots",
    "accessCode": "1234",
    "petInfo": "friendly dog, will secure before arrival"
  }
}

Equipment Rentals

For tracking rental details:

{
  "CustomFields": {
    "equipmentType": "kayak-single",
    "helmSize": "large",
    "experienceLevel": "beginner",
    "emergencyContact": "+15550999",
    "liabilityWaiverSigned": "true"
  }
}

Data Types and Formatting

String Values Only

All CustomFields values are strings. Store other types as strings:

{
  "CustomFields": {
    "attendeeCount": "5",          // number as string
    "confirmed": "true",            // boolean as string
    "preferredDate": "2025-12-15",  // date as string
    "arrivalTime": "09:45",         // time as string
    "metadata": "{\"key\":\"value\"}" // JSON as escaped string
  }
}

Parse values in your application logic.

Multi-Value Fields

For fields with multiple values, use delimiters:

{
  "CustomFields": {
    "selectedAddOns": "addon1,addon2,addon3",
    "allergies": "peanuts|shellfish|dairy"
  }
}

Split on delimiter (, or |) in your application.

Null or Empty Values

To "unset" a field:

{
  "CustomFields": {
    "fieldToRemove": ""
  }
}

Or omit the field when updating. Empty strings are stored as-is.

Validation

No Built-In Validation

OnSched doesn't validate CustomFields—you can store any key-value pairs. Validation is your responsibility:

  • Check required fields in your application
  • Validate formats (email, phone, date)
  • Enforce business rules (e.g., insurance required for certain services)
  • Sanitize user input

Field Name Conventions

Use clear, consistent naming:

  • camelCaseemergencyContactName
  • snake_caseemergency_contact_name
  • kebab-caseemergency-contact-name (less common)

Pick one convention and stick to it.

Size Limitations

While there's no strict limit per field, keep values reasonable:

  • Short answers – Under 255 characters
  • Long text – Use appointment notes field instead
  • File uploads – Store URLs, not file contents

CustomFields aren't designed for large blobs of data.

Security and Privacy

Sensitive Data

Be cautious storing sensitive information:

  • PII (personally identifiable information) – Secure your database
  • Payment details – Never store credit card numbers in CustomFields
  • Health information – Ensure HIPAA compliance if applicable

CustomFields are stored in plaintext. Encrypt sensitive data before storing if necessary.

Access Control

CustomFields are visible to:

  • Authenticated company users via API
  • Public appointment endpoints (if exposed)

Don't store internal-only data in CustomFields on public-facing appointments.

Integration Patterns

CRM Sync

Store CRM IDs for bi-directional sync:

{
  "CustomFields": {
    "salesforceContactId": "003xx000004TmiD",
    "salesforceLeadSource": "Website",
    "lastSyncedAt": "2025-11-25T12:34:56Z"
  }
}

Query appointments by CRM ID to update records.

Analytics Tracking

Capture attribution data:

{
  "CustomFields": {
    "utmSource": "google",
    "utmMedium": "cpc",
    "utmCampaign": "spring-promo",
    "referralCode": "FRIEND20"
  }
}

Export appointments with CustomFields for reporting.

Order Management

Link appointments to orders:

{
  "CustomFields": {
    "orderId": "ORD-2025-1234",
    "orderTotal": "150.00",
    "paymentStatus": "paid",
    "invoiceUrl": "https://example.com/invoices/1234"
  }
}

Track fulfillment status via appointments.

Searching and Filtering

Limitations

OnSched's API doesn't currently support filtering appointments by CustomFields directly in list endpoints.

Workaround:

  1. Retrieve appointments via GET /v3/appointments
  2. Filter by CustomFields in your application

Example (pseudocode):

const appointments = await fetchAppointments();
const filtered = appointments.filter(apt => 
  apt.CustomFields.referralSource === 'google'
);

Future Enhancements

Check API documentation for updates on CustomFields search capabilities.

Best Practices

Plan Your Schema

Document CustomFields before implementation:

Field Name            | Type    | Required | Example
----------------------|---------|----------|------------------
howDidYouHear         | string  | no       | "google"
specialRequests       | string  | no       | "wheelchair access"
confirmationPreference| string  | yes      | "email" or "sms"

Consistency across appointments makes reporting easier.

Use Descriptive Names

Avoid cryptic keys:

  • Bad: cf1, data, temp
  • Good: referralSource, dietaryRestrictions, emergencyContact

Future you (and your team) will thank you.

Don't Overload CustomFields

For extensive data, consider:

  • Appointment notes – Long-form text
  • Customer CustomFields – Profile data that persists across appointments
  • External database – Link via ID in CustomFields

Keep CustomFields lean and focused.

Version Your Schema

If CustomFields schema evolves:

{
  "CustomFields": {
    "schemaVersion": "2",
    "field1": "value1"
  }
}

This helps when migrating data or supporting multiple versions.

Test Thoroughly

Before going live:

  • Create appointments with various CustomFields
  • Retrieve and verify fields are present
  • Update appointments and confirm changes persist
  • Handle missing or malformed fields gracefully

Troubleshooting

CustomFields not appearing in response

Check:

  1. Fields were included in create/update request
  2. Request succeeded (check status code)
  3. Appointment is from your company (not another company's data)
  4. Using authenticated endpoints (public endpoints may omit CustomFields)

CustomFields lost after update

Cause: Updates replace CustomFields entirely, not merge them.

Solution: Always include all CustomFields in updates, even unchanged ones.

Cannot filter appointments by CustomField

Limitation: Direct API filtering by CustomFields isn't supported.

Solution: Filter in application layer after fetching appointments.

CustomFields appearing as null

Cause: Appointment was created without CustomFields.

Behavior: OnSched returns {} for empty CustomFields, not null. If you're seeing null, check response parsing logic.

Common Questions

Can I change field names after creating appointments?

CustomFields are flexible—you can add new fields anytime. Old appointments retain their original fields.

To "rename" a field across appointments, write a migration script.

Is there a limit on number of fields?

No hard limit, but keep it reasonable (typically under 20 fields). Excessive fields increase payload size and complexity.

Can I store arrays or objects?

Not natively. Serialize as JSON strings:

{
  "CustomFields": {
    "selectedOptions": "[\"option1\",\"option2\"]"
  }
}

Parse in your application.

Are CustomFields indexed for search?

Yes, CustomFields are indexed in PostgreSQL HSTORE, allowing efficient queries at the database level. However, API endpoints don't currently expose CustomField filtering directly.

Can customers see CustomFields?

Depends on your implementation:

  • Dashboard: Customers see whatever you display
  • API: Public endpoints may include or exclude CustomFields based on configuration

Control visibility in your UI layer.

Related Documentation

Custom Fields unlock powerful customization without requiring schema changes. Plan your fields thoughtfully, validate in your application, and use them to tailor OnSched to your exact business needs.