Download OpenAPI specification:
The Quinable Partner API lets an agency's external systems (e.g. a scheduling tool) create clients and routes/visits in Quinable and receive lifecycle status by signed webhooks.
Authentication. Every request carries a per-agency secret key in the Authorization header (Bearer qk_live_…). The key's prefix is its mode: qk_live_ acts on real data; qk_test_ is a sandbox — test routes never reach caregivers and move no money.
Conventions. JSON bodies; money is integer cents; ids are opaque prefixed strings (route_123); times of day are HH:MM in the route's local timezone. POST endpoints accept an Idempotency-Key header (replayed for 24h). List endpoints use cursor pagination (limit, starting_after). Errors use real HTTP status codes plus a typed body.
| external_ref | string Return the client with this partner-supplied id. |
| archived | boolean Default: false |
| limit | integer [ 1 .. 100 ] Default: 25 |
| starting_after | string A cursor — the id of the last object from the previous page. |
{- "object": "list",
- "data": [
- {
- "id": "client_841",
- "object": "client",
- "first_name": "string",
- "last_name": "string",
- "email": "string",
- "mobile": "string",
- "address": {
- "line1": "string",
- "line2": "string",
- "city": "string",
- "state": "string",
- "zip": "string"
}, - "timezone": "America/Detroit",
- "external_ref": "string",
- "archived": true,
- "livemode": true,
- "created_at": "2019-08-24T14:15:22Z"
}
], - "has_more": true
}| Idempotency-Key | string <= 191 characters Retrying with the same key replays the original response (24h) and creates nothing. |
| first_name required | string |
| last_name required | string |
string | |
| mobile | string |
| external_ref | string Your own id for this client. Unique per agency. |
required | object |
{- "first_name": "string",
- "last_name": "string",
- "email": "string",
- "mobile": "string",
- "external_ref": "string",
- "address": {
- "line1": "string",
- "line2": "string",
- "city": "string",
- "state": "string",
- "zip": "string"
}
}{- "id": "client_841",
- "object": "client",
- "first_name": "string",
- "last_name": "string",
- "email": "string",
- "mobile": "string",
- "address": {
- "line1": "string",
- "line2": "string",
- "city": "string",
- "state": "string",
- "zip": "string"
}, - "timezone": "America/Detroit",
- "external_ref": "string",
- "archived": true,
- "livemode": true,
- "created_at": "2019-08-24T14:15:22Z"
}| id required | string Example: client_841 |
{- "id": "client_841",
- "object": "client",
- "first_name": "string",
- "last_name": "string",
- "email": "string",
- "mobile": "string",
- "address": {
- "line1": "string",
- "line2": "string",
- "city": "string",
- "state": "string",
- "zip": "string"
}, - "timezone": "America/Detroit",
- "external_ref": "string",
- "archived": true,
- "livemode": true,
- "created_at": "2019-08-24T14:15:22Z"
}| status | string Enum: "open" "filled" "canceled" |
| date | string <date> |
| date_from | string <date> |
| date_to | string <date> |
| external_ref | string |
| limit | integer [ 1 .. 100 ] Default: 25 |
| starting_after | string A cursor — the id of the last object from the previous page. |
{- "object": "list",
- "data": [
- {
- "id": "route_10312",
- "object": "route",
- "status": "open",
- "name": "string",
- "description": "string",
- "date": "2019-08-24",
- "timezone": "America/Detroit",
- "start_time": "08:00",
- "end_time": "12:30",
- "confirmation_type": "agency_confirm",
- "profession_id": 0,
- "total_pay": 0,
- "currency": "usd",
- "external_ref": "string",
- "visits": [
- {
- "id": "visit_55810",
- "object": "visit",
- "client": "client_841",
- "start_time": "08:00",
- "end_time": "10:00",
- "pay_type": "per_hour",
- "hourly_rate": 0,
- "visit_amount": 0,
- "mileage_reimbursement": 0,
- "parking_reimbursement": 0,
- "note": "string",
- "overnight": true
}
], - "bids": {
- "pending": 0,
- "accepted": {
- "id": "bid_9917",
- "object": "bid",
- "route": "route_10312",
- "status": "pending",
- "amount": 0,
- "currency": "usd",
- "provider": {
- "id": "prov_60091",
- "first_name": "string",
- "last_name": "string",
- "mobile": "string",
- "email": "string",
- "rating": 0,
- "reliability_score": 0
}, - "placed_at": "2019-08-24T14:15:22Z",
- "status_changed_at": "2019-08-24T14:15:22Z"
}, - "data": [
- {
- "id": "bid_9917",
- "object": "bid",
- "route": "route_10312",
- "status": "pending",
- "amount": 0,
- "currency": "usd",
- "provider": {
- "id": "prov_60091",
- "first_name": "string",
- "last_name": "string",
- "mobile": "string",
- "email": "string",
- "rating": 0,
- "reliability_score": 0
}, - "placed_at": "2019-08-24T14:15:22Z",
- "status_changed_at": "2019-08-24T14:15:22Z"
}
]
}, - "canceled_at": "2019-08-24T14:15:22Z",
- "livemode": true,
- "created_at": "2019-08-24T14:15:22Z"
}
], - "has_more": true
}Creates a route with its visits in one call and posts it to the caregiver marketplace. Recurrence is the caller's responsibility — post one route per date, each with its own Idempotency-Key.
| Idempotency-Key | string <= 191 characters Retrying with the same key replays the original response (24h) and creates nothing. |
| name required | string |
| date required | string <date> |
| confirmation_type required | string Enum: "agency_confirm" "instant_confirm" |
| profession_id required | integer A value from GET /professions. |
| description | string |
| external_ref | string Your own id for this route. Unique per agency. |
required | Array of objects (VisitCreate) non-empty |
{- "name": "string",
- "date": "2019-08-24",
- "confirmation_type": "agency_confirm",
- "profession_id": 0,
- "description": "string",
- "external_ref": "string",
- "visits": [
- {
- "client": "client_841",
- "start_time": "08:00",
- "end_time": "10:00",
- "pay_type": "per_hour",
- "hourly_rate": 0,
- "visit_amount": 0,
- "mileage_reimbursement": 0,
- "parking_reimbursement": 0,
- "note": "string",
- "overnight": true
}
]
}{- "id": "route_10312",
- "object": "route",
- "status": "open",
- "name": "string",
- "description": "string",
- "date": "2019-08-24",
- "timezone": "America/Detroit",
- "start_time": "08:00",
- "end_time": "12:30",
- "confirmation_type": "agency_confirm",
- "profession_id": 0,
- "total_pay": 0,
- "currency": "usd",
- "external_ref": "string",
- "visits": [
- {
- "id": "visit_55810",
- "object": "visit",
- "client": "client_841",
- "start_time": "08:00",
- "end_time": "10:00",
- "pay_type": "per_hour",
- "hourly_rate": 0,
- "visit_amount": 0,
- "mileage_reimbursement": 0,
- "parking_reimbursement": 0,
- "note": "string",
- "overnight": true
}
], - "bids": {
- "pending": 0,
- "accepted": {
- "id": "bid_9917",
- "object": "bid",
- "route": "route_10312",
- "status": "pending",
- "amount": 0,
- "currency": "usd",
- "provider": {
- "id": "prov_60091",
- "first_name": "string",
- "last_name": "string",
- "mobile": "string",
- "email": "string",
- "rating": 0,
- "reliability_score": 0
}, - "placed_at": "2019-08-24T14:15:22Z",
- "status_changed_at": "2019-08-24T14:15:22Z"
}, - "data": [
- {
- "id": "bid_9917",
- "object": "bid",
- "route": "route_10312",
- "status": "pending",
- "amount": 0,
- "currency": "usd",
- "provider": {
- "id": "prov_60091",
- "first_name": "string",
- "last_name": "string",
- "mobile": "string",
- "email": "string",
- "rating": 0,
- "reliability_score": 0
}, - "placed_at": "2019-08-24T14:15:22Z",
- "status_changed_at": "2019-08-24T14:15:22Z"
}
]
}, - "canceled_at": "2019-08-24T14:15:22Z",
- "livemode": true,
- "created_at": "2019-08-24T14:15:22Z"
}Returns the route with its visits and the full bid list (bids.data) including provider decision info.
| id required | string Example: route_10312 |
{- "id": "route_10312",
- "object": "route",
- "status": "open",
- "name": "string",
- "description": "string",
- "date": "2019-08-24",
- "timezone": "America/Detroit",
- "start_time": "08:00",
- "end_time": "12:30",
- "confirmation_type": "agency_confirm",
- "profession_id": 0,
- "total_pay": 0,
- "currency": "usd",
- "external_ref": "string",
- "visits": [
- {
- "id": "visit_55810",
- "object": "visit",
- "client": "client_841",
- "start_time": "08:00",
- "end_time": "10:00",
- "pay_type": "per_hour",
- "hourly_rate": 0,
- "visit_amount": 0,
- "mileage_reimbursement": 0,
- "parking_reimbursement": 0,
- "note": "string",
- "overnight": true
}
], - "bids": {
- "pending": 0,
- "accepted": {
- "id": "bid_9917",
- "object": "bid",
- "route": "route_10312",
- "status": "pending",
- "amount": 0,
- "currency": "usd",
- "provider": {
- "id": "prov_60091",
- "first_name": "string",
- "last_name": "string",
- "mobile": "string",
- "email": "string",
- "rating": 0,
- "reliability_score": 0
}, - "placed_at": "2019-08-24T14:15:22Z",
- "status_changed_at": "2019-08-24T14:15:22Z"
}, - "data": [
- {
- "id": "bid_9917",
- "object": "bid",
- "route": "route_10312",
- "status": "pending",
- "amount": 0,
- "currency": "usd",
- "provider": {
- "id": "prov_60091",
- "first_name": "string",
- "last_name": "string",
- "mobile": "string",
- "email": "string",
- "rating": 0,
- "reliability_score": 0
}, - "placed_at": "2019-08-24T14:15:22Z",
- "status_changed_at": "2019-08-24T14:15:22Z"
}
]
}, - "canceled_at": "2019-08-24T14:15:22Z",
- "livemode": true,
- "created_at": "2019-08-24T14:15:22Z"
}Idempotent. A route with an accepted caregiver or recorded work returns 422 route_not_cancelable — unwind it in the Quinable portal.
| id required | string Example: route_10312 |
| Idempotency-Key | string <= 191 characters Retrying with the same key replays the original response (24h) and creates nothing. |
| reason | string |
{- "reason": "string"
}{- "id": "route_10312",
- "object": "route",
- "status": "open",
- "name": "string",
- "description": "string",
- "date": "2019-08-24",
- "timezone": "America/Detroit",
- "start_time": "08:00",
- "end_time": "12:30",
- "confirmation_type": "agency_confirm",
- "profession_id": 0,
- "total_pay": 0,
- "currency": "usd",
- "external_ref": "string",
- "visits": [
- {
- "id": "visit_55810",
- "object": "visit",
- "client": "client_841",
- "start_time": "08:00",
- "end_time": "10:00",
- "pay_type": "per_hour",
- "hourly_rate": 0,
- "visit_amount": 0,
- "mileage_reimbursement": 0,
- "parking_reimbursement": 0,
- "note": "string",
- "overnight": true
}
], - "bids": {
- "pending": 0,
- "accepted": {
- "id": "bid_9917",
- "object": "bid",
- "route": "route_10312",
- "status": "pending",
- "amount": 0,
- "currency": "usd",
- "provider": {
- "id": "prov_60091",
- "first_name": "string",
- "last_name": "string",
- "mobile": "string",
- "email": "string",
- "rating": 0,
- "reliability_score": 0
}, - "placed_at": "2019-08-24T14:15:22Z",
- "status_changed_at": "2019-08-24T14:15:22Z"
}, - "data": [
- {
- "id": "bid_9917",
- "object": "bid",
- "route": "route_10312",
- "status": "pending",
- "amount": 0,
- "currency": "usd",
- "provider": {
- "id": "prov_60091",
- "first_name": "string",
- "last_name": "string",
- "mobile": "string",
- "email": "string",
- "rating": 0,
- "reliability_score": 0
}, - "placed_at": "2019-08-24T14:15:22Z",
- "status_changed_at": "2019-08-24T14:15:22Z"
}
]
}, - "canceled_at": "2019-08-24T14:15:22Z",
- "livemode": true,
- "created_at": "2019-08-24T14:15:22Z"
}Test keys only. Drives a sandbox route through bid → accept → clock in/out → timesheet approved/paid, creating synthetic bids and timesheets and firing the bid.* / visit.* / timesheet.* webhooks — so you can exercise your receiver without a real caregiver or any money movement. A live key returns 400 not_in_test_mode.
| id required | string Example: route_10312 |
| until | string Default: "paid" Enum: "placed" "accepted" "clocked_in" "clocked_out" "approved" "paid" |
{- "until": "placed"
}{- "id": "route_10312",
- "object": "route",
- "status": "open",
- "name": "string",
- "description": "string",
- "date": "2019-08-24",
- "timezone": "America/Detroit",
- "start_time": "08:00",
- "end_time": "12:30",
- "confirmation_type": "agency_confirm",
- "profession_id": 0,
- "total_pay": 0,
- "currency": "usd",
- "external_ref": "string",
- "visits": [
- {
- "id": "visit_55810",
- "object": "visit",
- "client": "client_841",
- "start_time": "08:00",
- "end_time": "10:00",
- "pay_type": "per_hour",
- "hourly_rate": 0,
- "visit_amount": 0,
- "mileage_reimbursement": 0,
- "parking_reimbursement": 0,
- "note": "string",
- "overnight": true
}
], - "bids": {
- "pending": 0,
- "accepted": {
- "id": "bid_9917",
- "object": "bid",
- "route": "route_10312",
- "status": "pending",
- "amount": 0,
- "currency": "usd",
- "provider": {
- "id": "prov_60091",
- "first_name": "string",
- "last_name": "string",
- "mobile": "string",
- "email": "string",
- "rating": 0,
- "reliability_score": 0
}, - "placed_at": "2019-08-24T14:15:22Z",
- "status_changed_at": "2019-08-24T14:15:22Z"
}, - "data": [
- {
- "id": "bid_9917",
- "object": "bid",
- "route": "route_10312",
- "status": "pending",
- "amount": 0,
- "currency": "usd",
- "provider": {
- "id": "prov_60091",
- "first_name": "string",
- "last_name": "string",
- "mobile": "string",
- "email": "string",
- "rating": 0,
- "reliability_score": 0
}, - "placed_at": "2019-08-24T14:15:22Z",
- "status_changed_at": "2019-08-24T14:15:22Z"
}
]
}, - "canceled_at": "2019-08-24T14:15:22Z",
- "livemode": true,
- "created_at": "2019-08-24T14:15:22Z"
}{- "id": "bid_9917",
- "object": "bid",
- "route": "route_10312",
- "status": "pending",
- "amount": 0,
- "currency": "usd",
- "provider": {
- "id": "prov_60091",
- "first_name": "string",
- "last_name": "string",
- "mobile": "string",
- "email": "string",
- "rating": 0,
- "reliability_score": 0
}, - "placed_at": "2019-08-24T14:15:22Z",
- "status_changed_at": "2019-08-24T14:15:22Z"
}| route | string Example: route=route_10312 |
| date | string <date> Filter by clock-in calendar date (UTC). |
| status | string Enum: "pending" "approved" "rejected" |
| limit | integer [ 1 .. 100 ] Default: 25 |
| starting_after | string A cursor — the id of the last object from the previous page. |
{- "object": "list",
- "data": [
- {
- "id": "ts_4410",
- "object": "timesheet",
- "route": "string",
- "visit": "string",
- "bid": "string",
- "status": "pending",
- "pay_status": null,
- "clock_in_at": "2019-08-24T14:15:22Z",
- "clock_out_at": "2019-08-24T14:15:22Z",
- "duration_seconds": 0
}
], - "has_more": true
}| id required | string Example: ts_4410 |
{- "id": "ts_4410",
- "object": "timesheet",
- "route": "string",
- "visit": "string",
- "bid": "string",
- "status": "pending",
- "pay_status": null,
- "clock_in_at": "2019-08-24T14:15:22Z",
- "clock_out_at": "2019-08-24T14:15:22Z",
- "duration_seconds": 0
}Poll fallback and post-outage catch-up for webhooks. Requires any read scope.
| type | string Example: type=bid.accepted |
| created_after | string <date-time> |
| limit | integer [ 1 .. 100 ] Default: 25 |
| starting_after | string A cursor — the id of the last object from the previous page. |
{- "object": "list",
- "data": [
- {
- "id": "evt_01J5XV9GJ2M4",
- "object": "event",
- "type": "route.created",
- "created_at": "2019-08-24T14:15:22Z",
- "livemode": true,
- "data": {
- "object": { }
}
}
], - "has_more": true
}| id required | string Example: evt_01J5XV9GJ2M4 |
{- "id": "evt_01J5XV9GJ2M4",
- "object": "event",
- "type": "route.created",
- "created_at": "2019-08-24T14:15:22Z",
- "livemode": true,
- "data": {
- "object": { }
}
}{- "object": "list",
- "data": [
- {
- "id": "we_12",
- "object": "webhook_endpoint",
- "enabled_events": [
- "string"
], - "status": "enabled",
- "created_at": "2019-08-24T14:15:22Z"
}
], - "has_more": true
}The signing secret (qwhsec_…) is returned once, in this response only.
| url required | string <uri> An https:// URL. Internal/loopback targets are rejected. |
| enabled_events required | Array of strings Event types to receive, or ["*"] for all. |
{- "enabled_events": [
- "*"
]
}{- "id": "we_12",
- "object": "webhook_endpoint",
- "enabled_events": [
- "string"
], - "status": "enabled",
- "created_at": "2019-08-24T14:15:22Z",
- "secret": "qwhsec_xxx"
}