SchedulinqDocs
Go to Schedulinq

Planning links

The Plan button — mint a link when the caller clicks, send them to Schedulinq's planner with the lead already filled in, and come back to your CRM.

A planning link is the Plan button in your CRM. The person on the phone with the lead clicks it, and Schedulinq's planner opens with the customer, the team member, the service and the address already filled in. They pick a time, save the appointment, and go back to the CRM. What they see in Schedulinq is described in Find appointment options.

How it works

  1. When the caller clicks Plan, your backend creates a planning link with POST /api/v1/planning-links (scope planning_links:write). The answer is a 201 with id, url, expiresAt, customerId and — when you named a team member — userId.
  2. Your backend sends the caller's browser to url, for example with an HTTP 303. If they are not signed in to Schedulinq, they sign in first and land back on the link.
  3. The planner opens on the customer, the service, the team member (or everyone who does the service) and the address from your request.
  4. The caller picks a time and saves the appointment. It carries your metadata.
  5. Back to the CRM takes them back to the address you gave.

Example request · POST /api/v1/planning-links

IDEMPOTENCY_KEY=$(uuidgen)   # once per action; to retry, run only the curl line again (same key)
curl -X POST https://api.schedulinq.com/api/v1/planning-links \
  -H "Authorization: Bearer $SCHEDULINQ_API_KEY" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "appointmentTypeId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
  "customer": {
    "id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06"
  },
  "metadata": {
    "campaignId": "C-42",
    "dealId": "D-123"
  },
  "returnUrl": "https://crm.example.com/deals/D-123"
}'

Response · 201

{
  "customerId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06",
  "expiresAt": "2026-10-14T09:15:00+02:00",
  "id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e07",
  "url": "https://app.schedulinq.com/plan-link#EXAMPLE-TOKEN",
  "userId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e03"
}

Mint on click, never in advance

The url is a secret that opens the planner once, within 15 minutes. So create the link at the moment of the click, and send the browser there straight away.

  • Never put it in an e-mail, an SMS or a page rendered ahead of time, and never log it.
  • The token travels after the # of the URL, so it never reaches a server log or a Referer header.
  • Need the planner again later? Create a new link. Each request with a new Idempotency-Key makes a new link; see Idempotency.

At most 120 planning links a minute can be created per organisation, whatever the number of keys. See Rate limits.

The request

Request body · POST /api/v1/planning-links

{
  "appointmentTypeId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
  "customer": {
    "id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06"
  },
  "metadata": {
    "campaignId": "C-42",
    "dealId": "D-123"
  },
  "returnUrl": "https://crm.example.com/deals/D-123"
}
  • user is optional: the team member the planner opens on, {"id": …} or {"email": …}, exactly one of the two. They must be active and schedulable. Without user the planner opens on every team member who does the service, and the answer has no userId. Either way the caller may still pick another team member in the planner.
  • appointmentTypeId is the service the planner opens on (GET /api/v1/services). It is optional; the team member, when you name one, must be able to do it.
  • customer comes in one of two forms. By id — {"id": …} and no other field — it is one of your customers: one that was merged into another is followed to that one, and an id that is unknown, archived or another organisation's gives a 404 errors.api.customerNotFound. An id together with any other customer field gives a 400 errors.validation.customerIdOrDetails. By its details, email is required and the customer is found by that address, whatever its case, among your live customers; a customer that was merged into another is followed to that one. With several matches, a customer that is not deactivated wins, then the one changed most recently. On a customer that already exists, only empty names, company name and an empty phone number are filled in; nothing is overwritten. A new customer is created with the e-mail address in lower case. A PERSON (the default) needs lastName, a BUSINESS needs companyName. phone is E.164 (a + and the country code) or a national number in your organisation's country.
  • address is where this appointment happens, when it is at the customer's. It never replaces the customer's billing address and is not stored on the customer. line1 holds the street and the house number.
  • metadata are your metadata, such as a deal id. They are copied onto every appointment booked from the link. See Metadata.
  • returnUrl is where Back to the CRM goes; see below.

A customer you already know, by id, and no team member:

Request body · POST /api/v1/planning-links

{
  "appointmentTypeId": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e01",
  "customer": {
    "id": "3f0c8a52-6b1e-4d7a-9c3e-1a2b3c4d5e06"
  },
  "metadata": {
    "campaignId": "C-42",
    "dealId": "D-123"
  },
  "returnUrl": "https://crm.example.com/deals/D-123"
}

Who can open it

The link works for a team member of your organisation who may create appointments for everyone and see every customer. The first one who opens it owns it: if the same person opens it again within the 15 minutes — after a reload, or a lost connection — they get the same planner. Anyone else sees that the link has already been used. Someone from another organisation sees that the link does not exist.

Your CRM does not have to know the caller's Schedulinq account: whoever opens the link signs in to Schedulinq as themselves.

What the caller sees when it fails

Schedulinq shows one message per reason: the link has expired (after 15 minutes), has already been used, does not exist (another organisation, or a URL that arrived incomplete), or the caller lacks the rights to plan from the CRM. In every case the answer is the same: click Plan in the CRM again, so your backend mints a new link. See Find appointment options.

Back to your CRM

returnUrl must be an https URL whose origin is on your organisation's list of allowed return addresses. An admin keeps that list under Integrations → API, in the Allowed return addresses card below the keys. Any other URL is refused when the link is created, with a 400 errors.validation.returnUrlOriginNotAllowed. While the list is empty, every planning link with a returnUrl is refused.

Back to the CRM appears in the planner's banner and, after saving, at the top of the appointment's page — also after a reload. It is shown to whoever may plan, and only while the origin is still on the list: remove the origin and the button disappears.

The Allowed return addresses card on the API page with two addresses, each with Remove, and a box to add one.

Allowed. Every appointment created from the link — one, or several in a row — carries the link's metadata and its id as planningLinkId. They can be saved for up to 24 hours after the link was opened, and only for the link's customer.

Errors

The ones a CRM meets most:

Every code is listed under the operation in the reference.

Frequently asked questions

No. It opens Schedulinq's planner, which needs a signed-in team member of your organisation who may plan. To let the customer choose a time themselves, send an invitation.

The caller closed the tab. What now?

Click Plan again: your backend mints a new link. Reopening the same link only works for the same person, within its 15 minutes.

Is the team member locked in the planner?

No. The planner opens on the team member you named, but the caller can pick someone else. Reads and webhooks then show who was really booked. Leave user out and the planner opens on everyone who does the service.

Last updated October 4, 2026