Skip to content
onepick

Documentation

onepick is a connector for AI assistants. It has four tools: one finds the best local business for a job, one books it, one reports on a booking, and one deletes a person’s data.

Connect

The connector is served three ways from the same handlers, with the same sign-in.

  • MCP, over streamable HTTP: /api/mcp
  • REST, one route per tool: /api/tools
  • An OpenAPI document describing those routes: /openapi.json

All three are HTTPS only. Location and the person’s details are tool inputs or come from our own sign-in; nothing is assumed from the assistant.

Sign-in

Sign-in is OAuth with PKCE. The person signs in with a one-time code, so there is no password to set. In demo mode, emailed codes are off and the reviewer account signs in with a password. An unauthenticated request to /api/mcp returns 401 with OAuth resource metadata, which is how an assistant discovers where to send the person.

Find the one best local business

find_pick

Given what the person needs and a US location, returns one recommended business and why it was picked.

Inputs of find_pick

service
Type: text
Notes: Required. 1 to 200 characters. What the person needs, in their words, e.g. 'a haircut' or 'AC not cooling'.
category
Type: one of: haircut, lawn_care, house_cleaning, hvac, plumbing, electrical
Notes: Optional. Set this when you can tell which one applies; otherwise leave it out.
location
Type: text
Notes: Required. 1 to 200 characters. US zip code or 'City, ST'.

What comes back from find_pick

Every result has plain text the assistant can show as it is, and the structured data below. Each possible result is a status.

picked
One business was picked. The result carries its name, phone, address, distance and timezone, the criteria it was picked on, its booking link when it has one, whether a runner-up is held, and a disclosure saying the pick is not paid for.
pickId
Type: text
business
Type: object
business.name
Type: text
business.phone
Type: text
Notes: US phone number in E.164, +1 then 10 digits.
business.address
Type: text or null
business.website
Type: text or null
business.distanceKm
Type: number
business.timezone
Type: text
criteria
Type: list
Notes: At least 1 item.
criteria[].key
Type: one of: distance, licence, sources, website, online_booking, track_record
criteria[].label
Type: text
criteria[].detail
Type: text
bookingLink
Type: object or null
bookingLink.platform
Type: one of: booksy, vagaro, fresha, square, styleseat
bookingLink.url
Type: text
Notes: URL.
hasRunnerUp
Type: true or false
disclosure
Type: text
Notes: Not empty.
nothing_found
No pick. The reason says why: the location is not a US zip code or city we can place (unknown_location), the request is not one of the launch services (unsupported_service), or no suitable business was found near there (no_match).
reason
Type: one of: unknown_location, unsupported_service, no_match

Permissions of find_pick

Read-only: it changes nothing the person or any business can observe; the stored pick is our own copy of the search result, so it is safe to retry. Open world: it reads the business's public website.

Read-only
yes
Destructive
no
Idempotent
no
Open world
yes

Book the pick

book

Books the picked business for the person. First call it without a confirmation token to preview what will be shared, then again with the token to go ahead.

Inputs of book

pickId
Type: text
Notes: Required.
useRunnerUp
Type: true or false
Notes: Optional. Default false.
timeWindows
Type: list
Notes: Required. 1 to 5 items. Local wall-clock times in the business's timezone; no UTC offset needed.
timeWindows[].start
Type: text
Notes: Required. Local time, YYYY-MM-DDTHH:mm.
timeWindows[].end
Type: text
Notes: Required. Local time, YYYY-MM-DDTHH:mm.
firstName
Type: text
Notes: Required. 1 to 60 characters.
callbackPhone
Type: text
Notes: Required. US phone number in E.164, +1 then 10 digits.
confirmationToken
Type: text
Notes: Optional. Leave out to preview what will be shared; pass the token from the preview to go ahead.

What comes back from book

Every result has plain text the assistant can show as it is, and the structured data below. Each possible result is a status.

needs_confirmation
The preview. Nothing has been shared and nobody has been called. willShare lists exactly the details that would be spoken to the business, and confirmationToken is what to send back to go ahead.
businessName
Type: text
willShare
Type: list
Notes: At least 1 item.
confirmationToken
Type: text
calling
The booking exists and an AI call is being placed. Ask booking_status for the result.
bookingId
Type: text
businessName
Type: text
use_booking_link
The business takes bookings online. The person books through bookingLink, and no call is placed.
bookingId
Type: text
businessName
Type: text
bookingLink
Type: object
bookingLink.platform
Type: one of: booksy, vagaro, fresha, square, styleseat
bookingLink.url
Type: text
Notes: URL.
call_yourself
No AI call can be placed. The result carries the business's number, a short script to read out, the booking link when one exists, and the reason.
bookingId
Type: text
businessName
Type: text
reason
Type: one of: no_phone, mobile_number, line_type_unknown, excluded_state, outside_hours, calls_stopped, call_limit
phone
Type: text
Notes: US phone number in E.164, +1 then 10 digits.
script
Type: text
Notes: Not empty.
bookingLink
Type: object or null
bookingLink.platform
Type: one of: booksy, vagaro, fresha, square, styleseat
bookingLink.url
Type: text
Notes: URL.
retryAfter
Type: text or null
too_many_requests
The person has reached the limit of 5 bookings a day. retryAfter says when to try again.
retryAfter
Type: text
not_found
The pick is unknown or belongs to someone else, there is no runner-up to use, or the confirmation token has expired or belongs to someone else.

Permissions of book

A write, marked destructive: with a confirmation token it shares personal details with a business and may place a phone call, which cannot be undone. It is open world because it reaches a real business outside our own records. Repeating a token that was already used returns the same booking and does not place a second call.

Read-only
no
Destructive
yes
Idempotent
no
Open world
yes

Check a booking

booking_status

Returns the current state of a booking and, when the first business did not come through, the runner-up.

Inputs of booking_status

bookingId
Type: text
Notes: Required.

What comes back from booking_status

Every result has plain text the assistant can show as it is, and the structured data below. Each possible result is a status.

calling
The call is in progress. A call with no result after 15 minutes reads as failed.
confirmed
The business confirmed. confirmedStart is the appointment time with its UTC offset, and timezone is the business's timezone to show it in.
declined
The business said no. note may carry the next slot it offered.
no_answer
Nobody answered.
voicemail
The call reached voicemail. No message is left.
person_must_call
The business wants the person to call. This is also the result when the business asks for a price, a deposit or card details, which the call does not agree.
failed
The call could not be completed, or no result arrived within 15 minutes.
call_yourself
This booking was handed to the person to call, with a script.
use_booking_link
This booking was handed to the person as the business's own booking link.
bookingId
Type: text
pickId
Type: text
serviceText
Type: text
businessName
Type: text
businessPhone
Type: text
Notes: US phone number in E.164, +1 then 10 digits.
confirmedStart
Type: text or null
timezone
Type: text
note
Type: text or null
attempts
Type: whole number
Notes: 0 to 2.
runnerUp
Type: object or null
runnerUp.businessName
Type: text
runnerUp.distanceKm
Type: number
runnerUp.criteria
Type: list
Notes: At least 1 item.
runnerUp.criteria[].key
Type: one of: distance, licence, sources, website, online_booking, track_record
runnerUp.criteria[].label
Type: text
runnerUp.criteria[].detail
Type: text
runnerUp.disclosure
Type: text
Notes: Not empty.
not_found
There is no such booking for this person.

Permissions of booking_status

Read-only and idempotent, closed world: it reads our own record of the booking.

Read-only
yes
Destructive
no
Idempotent
yes
Open world
no

Delete my data

delete_my_data

Deletes the person's account, picks and booking history. Cannot be undone.

Inputs of delete_my_data

confirm
Type: must be true
Notes: Required.

What comes back from delete_my_data

Every result has plain text the assistant can show as it is, and the structured data below. Each possible result is a status.

This tool has one result and no status field.

deletedBookings
Type: whole number
Notes: 0 or more.
deletedPicks
Type: whole number
Notes: 0 or more.

Permissions of delete_my_data

Destructive and idempotent, closed world: it deletes the person's data, and running it again deletes nothing more.

Read-only
no
Destructive
yes
Idempotent
yes
Open world
no

The two-step book

book shares nothing and calls nobody without a preview first.

  1. Call book without a confirmationToken. The result is needs_confirmation: it names the business, lists exactly what will be shared, and carries a single-use token that lasts 30 minutes. No booking exists yet.
  2. Show the person that preview. When they agree, call book again with the same inputs and the token. Only this call creates a booking.

Repeating a call with a token that was already used returns the same booking and does not place a second call, so a retry is safe. A second attempt at the same business happens only when the person asks again, with a fresh preview and token. To book the runner-up after the first business did not come through, call book with the same pickId and useRunnerUp set to true.

Errors

Every result a person can cause, or needs to hear about, is a status in the tool’s result, not an error. Inputs that do not match the tables above are rejected before the tool runs, with a message naming the field.

A fault on our side returns one fixed message, with isError set over MCP and 500 over REST: “Something went wrong on our side. Please try again.” It carries no stack trace, vendor name or internal id.

Limits

Limits
RuleLimit
Bookings per person per day5
Call attempts per business for one request2
Calls to one business in any 30 days, counted by phone number3
Minutes a confirmation token lasts30
Minutes before a call with no result reads as failed15

When no AI call is placed

AI calls are placed only between 9am and 6pm in the business’s local time, only to landline and internet phone numbers unless the business has given verified consent, and not to businesses in Indiana, Louisiana, Minnesota, Montana and Nebraska. In every other case book returns call_yourself with one of these reasons.

no_phone
The business has no usable phone number.
mobile_number
The number is a mobile number. Without the business's verified consent, only landline and internet phone numbers are called.
line_type_unknown
The kind of line could not be determined, so the number is treated as one we may not call.
excluded_state
The business is in a state where no AI booking call is placed: Indiana, Louisiana, Minnesota, Montana and Nebraska.
outside_hours
It is outside 9am to 6pm in the business's local time. retryAfter says when a call becomes possible.
calls_stopped
The business asked us to stop AI calls to this number. That request is permanent.
call_limit
The business has had its limit of calls: 2 attempts for one request, or 3 calls in any 30 days.

Example prompts

These work:

  • I need a haircut near 07030 on Friday after 5pm. Pick a barber and book it.
  • My AC stopped cooling. Find an HVAC company in Hoboken, NJ and book the earliest visit this week.
  • Find a house cleaner near Austin, TX 78704 and book a first clean for Saturday morning.
  • Book a plumber for a leaking kitchen tap in Tampa, FL, any weekday between 9 and noon.
  • Did my lawn care booking go through? If not, try the next business.

These are declined:

Book me a dentist appointment for a cleaning next week.
Health and dental bookings are out of scope. find_pick returns nothing_found with the reason unsupported_service.
Get me a table for four at an Italian place tonight.
Restaurant bookings are out of scope. find_pick returns nothing_found with the reason unsupported_service.
Find a plumber near Toronto, ON.
Only US locations are served. find_pick returns nothing_found with the reason unknown_location.

Data sources and notices

onepick's directory of businesses is built from open data. This file carries the notices those licences ask for.

No Google Places or Yelp content is used.

Business listings: Overture Maps Foundation, places theme

Business names, addresses, phone numbers, websites and locations come from the Overture Maps Foundation places theme, https://overturemaps.org. Each stored business records which upstream datasets it came from (dataSources, for example overture-places:meta).

The places theme combines several sources, each under its own licence (https://docs.overturemaps.org/attribution/):

Data sources and their licenses
SourceLicence
Meta, Microsoft, PinMeTo, Krick, RenderSEO, DAC, BrightQueryCommunity Data License Agreement, Permissive, Version 2.0 (CDLA-Permissive-2.0)
FoursquareApache License, Version 2.0, with the NOTICE below
AllThePlacesCC0 1.0 Universal

The places theme contains no OpenStreetMap data.

Changes made by onepick

The data is changed before it is stored or shown: only places in the United States in six service categories are kept; places marked permanently closed are dropped; phone numbers are rewritten in E.164 form; duplicate listings of one business are merged; a timezone is derived from each location.

CDLA-Permissive-2.0

Section 2.1 of the agreement: "A Data Recipient may share Data, with or without modifications, so long as the Data Recipient makes available the text of this agreement with the shared Data."

The text of the agreement: https://cdla.dev/permissive-2-0/

Foursquare OS Places

Overture's note on these rows: Foursquare data was transformed to the Overture schema.

The Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0

The full content of Foursquare's NOTICE.txt, as published at https://opensource.foursquare.com/places-notice-txt/ and read on 2026-09-21, unmodified:

© 2026 Foursquare Labs, Inc. All rights reserved.

The Foursquare OS Places dataset (the “Data”) is licensed under the Apache License, Version 2.0 (the “License”). You may not use, modify, or distribute the Data except in compliance with the License.

As set forth more fully in the License, if you use, modify, or distribute the Data, you must:
– provide recipients with a copy of the License.
– if applicable, include prominent notices to the extent you’ve changed the Data.
– preserve attribution to Foursquare, including preserving the full content of this NOTICE.txt file.

To ensure appropriate attribution to Foursquare, we recommend the following:
– if using/distributing the Data in flat file form as-is or after making changes/modifications: include this NOTICE.txt file, which may be modified to include an additional notice of your changes/modifications, if any.
– if using/distributing the Data in API form as-is or after making changes/modifications: include a copy of the content from this NOTICE.txt file prominently in your developer documentation for such API, which may be modified to include an additional notice of your changes/modifications, if any.

You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable law or agreed to in writing, the Data distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

See the License for the specific language governing permissions and limitations under the License.

We also encourage you to join our Placemaker community where you can contribute and provide suggestions to improve the accuracy of the Data for future releases for yourself and others.

Notice of changes, as that file invites: onepick's changes to the data are listed under "Changes made by onepick" above.

CC0 1.0 Universal

AllThePlaces rows are dedicated to the public domain: https://creativecommons.org/publicdomain/zero/1.0/

Zip codes and city names: United States Census Bureau

Zip code centroids, city names and their states come from the Census Bureau's 2025 gazetteer files and the 2020 ZCTA-to-county relationship file. Works of the United States government are in the public domain; no licence applies. This product uses Census Bureau data but is not endorsed or certified by the Census Bureau.

Timezones

Each business's and each location's timezone name (for example America/New_York) is worked out from its coordinates, offline, with the tz-lookup package. The package is published under CC0 1.0. Its boundary data comes from the timezone-boundary-builder project (https://github.com/evansiroky/timezone-boundary-builder), whose output is licensed under the Open Data Commons Open Database License (ODbL), https://opendatacommons.org/licenses/odbl/, and is derived from OpenStreetMap.

Timezone boundaries © OpenStreetMap contributors, https://www.openstreetmap.org/copyright.

onepick stores only the resulting timezone name beside each record; it does not store or redistribute the boundary data.

State license data

Whether a trade business holds a state license is checked against public license files published by three state agencies. Each stored license records the file it came from and the day that file was copied (source_file, source_date).

Changes made by onepick

Only heating and air conditioning, plumbing and electrical business licenses are kept. Names are normalized for matching, phone numbers are rewritten in E.164 form, and each agency's own status codes are reduced to "active" or "inactive". A license's current standing can change at any time: the agency's own lookup is the authority.

Texas Department of Licensing and Regulation (TDLR)

Source: Texas Department of Licensing and Regulation, https://www.tdlr.texas.gov, license data files at https://www.tdlr.texas.gov/LicenseSearch/licfile.asp. The date each record was copied is stored with it; the files are re-copied on every import.

This information is copied from the TDLR website. Neither onepick nor the way this information is presented is endorsed by the State of Texas or by TDLR. No TDLR logo is used.

Current status of any Texas license: https://www.tdlr.texas.gov/LicenseSearch/

California Contractors State License Board (CSLB)

Source: CSLB License Master file, https://www.cslb.ca.gov/onlineservices/dataportal/ContractorList. Information on State of California websites is, unless otherwise indicated, in the public domain (https://www.ca.gov/use/); no credit is required. The State of California makes no guarantee of its accuracy and does not endorse onepick.

Current status of any California license: https://www.cslb.ca.gov/OnlineServices/CheckLicenseII/CheckLicense.aspx

Florida Department of Business and Professional Regulation (DBPR)

Source: DBPR construction and electrical licensee files, https://www2.myfloridalicense.com/construction-industry/public-records/ and https://www2.myfloridalicense.com/electrical-contractors/public-records/, provided as public records under Chapter 119, Florida Statutes; no credit is required. DBPR does not endorse onepick.

Current status of any Florida license: https://www.myfloridalicense.com/wl11.asp