Scheduling
schedulingRules, scheduleCall, scheduledCalls, cancelScheduledCall and the SDK's booking sheet on every platform, React Native's useGraineScheduling, and the chat's upcoming calls — parameters, the checks made on the device first, what each returns and when, every error, and side effects.
How scheduled calls behave — who may book, when they ring, statuses — is on
Scheduled calls. A call booked here rings the
identified customer's app through hosted ringing; a call booked from a website
rings their app too, and ends no_device when they have none.
schedulingRules
What the agent allows, read from its settings when the SDK loads them. A property: no request.
Returns GraineSchedulingRules.
Before the agent's settings have loaded, and from an older server, it holds the
defaults — usersCanBook: false, 5 minutes, 14 days, 3 waiting, 45 seconds.
earliest(now) and latest(now) give the first and last times a booking may
take. Android adds ringsAndroid, and iOS's hosted-calls settings
ringsThisPlatform: whether Graine can ring this platform.
scheduleCall
Books a call for the customer.
A string at is ISO-8601 with its offset; a number is milliseconds since the
epoch.
| Parameter | Type | Required | Limits | |
|---|---|---|---|---|
at | A date | Yes | From earliest(now) to latest(now), inside the agent's calling window | Sent with the device's offset and time zone |
reason | String | No | 120 characters | What the call is about. It reaches the agent when the call connects and never travels in the ring. Kotlin Multiplatform trims it to 120; Flutter refuses a longer one on the device with invalid_request |
Checks on the device first, before any request, from schedulingRules:
scheduling_off when usersCanBook is false, too_soon before earliest(now)
(React Native allows 30 seconds' grace), too_far after latest(now).
Returns the booked GraineScheduledCall,
status scheduled.
Resolves when Graine has booked it.
Errors on the device: scheduling_off, too_soon, too_far (and Flutter's
invalid_request). From Graine: outside_window, too_many_pending,
device_limit, identity_required, invalid_request, rate_limited (at most 10
bookings an hour from one install), network, unavailable —
Errors.
Side effects the call is booked for the identified customer when this device
is one of theirs, otherwise for this install. React Native emits
scheduled_calls_changed ({ booked: id }).
Threading Android calls done on the main thread.
scheduledCalls
The customer's — or this install's — upcoming calls.
Returns a list, upcoming first. Errors network, rate_limited,
unavailable, invalid_key, origin_refused.
cancelScheduledCall
Returns the call's status afterwards — cancelled. Errors not_found
(not this customer's or install's, or no such call), network, rate_limited,
unavailable. Only a call whose canCancel is true can be cancelled.
Side effects React Native emits scheduled_calls_changed ({ cancelled: id }).
useGraineScheduling
React Native's hook for a booking screen.
It loads the upcoming calls when it mounts and again after each booking or
cancellation, and follows rules as the agent's settings load.
bookingAvailable and openBooking are the SDK's booking sheet — see
Booking a call.
Booking a call
The SDK's own booking sheet: the day, the time, what the call is about and Book call, drawn in your agent's design — "Call me at…" with no screen of your own. It resolves with the booked call when the sheet closes, or nothing when the customer closed it without booking.
The provider draws the sheet openBooking asks for. The time is a grid of
times every 15 minutes.
| Parameter | Type | Required | Limits | |
|---|---|---|---|---|
reason | String | No | 120 characters | Fills "What's it about? (optional)"; the customer may change it |
bookingAvailable is true only when all of these hold — the same rule on every platform:
- the agent lets users book (
schedulingRules.usersCanBook); - Graine can ring this phone: your workspace has In-app calling set up
(
hostedRinging) for this platform (platformsempty, or naming it). A website cannot be rung, so there this rule does not apply: the booking rings the customer's app; - the customer is identified with the trait your Who is the user? setting
names. Without it a booking is refused with
identity_required.
What the sheet offers. A chip per day, from today to the last day
maxDaysAhead allows ("Today", "Tomorrow", then the weekday and date); times
from earliest(now) rounded up to 5 minutes to latest(now) rounded down to
5 minutes, and no day that has no time left in it; the phone's time zone beside
the time chosen.
Refusals stay in the sheet, as one line under Book call:
| Code | The line |
|---|---|
too_soon | Pick a time at least 5 minutes from now (the agent's lead time). The sheet moves to the new earliest time |
too_far | Pick a time within 14 days (the agent's limit) |
outside_window | That time is outside the hours we can call — pick another |
too_many_pending | You already have 3 calls booked — cancel one first (the agent's limit) |
scheduling_off | Booking a call isn't available right now |
identity_required | Sign in to book a call |
| anything else | Couldn't book just now — try again |
scheduleCall refuses scheduling_off, too_soon and too_far in the same words
before anything is sent — the message of the error it throws — on every SDK and
the website.
Opened where bookingAvailable is false, the sheet opens, shows the
scheduling_off or identity_required line and does not book.
Once it is booked the sheet says "Booked — Acme will call you on Wed 14 Oct, 6:00 PM" with Done — always the date, never "Today". The name is the agent's call name, else the chat's title, else the heading.
The chat's upcoming calls
The SDKs' chat window lists the customer's upcoming calls with a cancel button
and — where bookingAvailable is true and the design keeps the booked-calls
card — Book a call, which opens the booking sheet: under the booked calls,
or on its own where none are booked. The same list is yours to read:
| Platform | List | Refresh | Cancel |
|---|---|---|---|
| iOS | chat.upcomingCalls: [GraineScheduledCall] (published) | chat.refreshUpcomingCalls() | chat.cancelUpcomingCall(id:) |
| Android | chat.upcomingCalls: List<GraineScheduledCall> | chat.refreshUpcomingCalls() | chat.cancelUpcomingCall(id) |
| Flutter | chat.upcomingCalls | chat.refreshUpcoming() | chat.cancelUpcoming(id), with chat.cancelStateOf(id) → none, cancelling or failed |
These update the list in place and report failures in the list's own state rather than throwing.

