Authentication
First of all, you must request an API token to be able to query the Yoplanning API.
To do so, please request a token in your Yoplanning settings (section "Advanced")
Once you have your token in hands, you can start working with the API. All API methods require authentication.
We use standard token-based authentication system. To authenticate, just provide your API token on the header
of each request like so:
Authorization: Token 4804c2cb4d87a13146d4de029f407c82149f2ada
Be careful : the space between "Token" and the token is important.
Here is a full example using curl
curl -H "Content-Type: application/json" https://yoplanning.pro/api/v3.1/teams/5a90332e-568f-4980-9859-88a984844a4d/clients/8d23503e-041e-4180-98d1-641183bc5ead -H 'Authorization: Token 4804c2cb4d87a13146d4de029f407c82149f2ada'
If you don't provide a token or if the token is invalid, the API will respond with a 401 HTTP code
(Unauthorized) and will give you a "details" field in the response JSON to help you understand the problem.
{"detail":"Invalid token."} or
{"detail":"Authentication credentials were not provided."}
Permissions
When you have requested your API token, you have been granted specific permissions on a certain set
of teams.
This means you probably cannot use all the API methods listed.
If you call the Yoplanning API without permissions (for instance, you don't have permission to create
session-groups on the team),
the API will respond with a 403 HTTP code (Forbidden) with the following details.
{"detail":"You do not have permission to perform this action."}
Throttling
For security reasons, the number of requests you can perform is limited, per user (identified by your token) and
per clock hour: the counters start again from zero at the top of every hour. Reads (
GET,
HEAD,
OPTIONS) and writes (
POST,
PUT,
PATCH,
DELETE) are counted separately, and each user is on one of these tiers:
- Tier 1 (default) :
1000 reads and 100 writes per hour
- Tier 2 :
8000 reads and 200 writes per hour
- Tier 3 :
20000 reads and 1000 writes per hour
- Tier 4 :
50000 reads and 2000 writes per hour
Whatever the tier, a user cannot perform more than
50000 requests per hour in total.
When the limit applies to your token, each response tells you where you stand in the current hour, for the kind
of request you made:
X-RateLimit-Limit : the number of requests of that kind allowed per hour
X-RateLimit-Remaining : how many are left in the current hour
X-RateLimit-Reset : when the counters start again from zero (Unix timestamp, in seconds)
Beyond the limit, the server will respond with a 429 HTTP code (Too Many Requests) with the following details :
{"detail":"Request was throttled. Expected available in 1260 seconds."}
If your use case needs a higher tier, please contact our support.
Concurrent requests
Independently of these rates, the number of requests you can have
in progress at the same time
(sent, but not answered yet) is limited:
- 100 concurrent requests per token
- 100 concurrent requests per IP address, whatever the tokens used
Sending requests one after the other never reaches this limit; firing many of them in parallel does. Beyond it,
the server responds immediately with a 429 HTTP code (Too Many Requests), a
Retry-After: 1 header and
the following details :
{"detail": "Too many concurrent requests, retry later."}
Wait for some of your requests to complete before sending new ones: cap the parallelism of your client (a pool
of a few workers is plenty) and retry a refused request after the
Retry-After delay.
If you need higher limits for a specific purpose, please contact our support.
Concepts
Team
The
team is the most fundamental concept in Yoplanning (which is a collaborative tool).
Almost all methods take a team_id parameter in the url. That means all the actions are relative to a team.
UUID
Lots of methods requires an "id" parameter in the url. This is the unique identifier for the resource you are trying
to retrieve / create / update / delete.
Yoplanning uses UUID (version 4) as unique identifier for all resources.
Endpoints and HTTP verbs
For many resources, 2 endpoints are available :
- one to access a
specific instance. The endpoint basically ends with {id}, for example :
/api/v3.1/teams/{teamId}/clients/{id}/
You can call these endpoints with the following http verbs :
- GET : Retrieve the resource
- PUT : Update the resource
- DELETE : Delete the resource
- one to access the
instances manager : You call these endpoints with the following http verbs :
/api/v3.1/teams/{teamId}/clients
- GET : get a list of all resources (see Pagination)
- POST : create a new resource (The id will be generated by the server)
All these actions are not available for all endpoints. Please see each endpoint documentation to know which methods
are allowed.
Using PUT properly
When you want to update a resource, you should
always use the following workflow :
- Retrieve the resource with a GET request
- Modify the JSON data
- Send a PUT request with the updated JSON
If you don't, the API may reject you because some nested resources require an ID that you don't give.
In addition, you may loose some data.
Online selling
For online selling purposes (retrieving products and availabilities), only 2 endpoints should be used:
/api/v3.1/teams/{teamId}/online-products/
/api/v3.1/teams/{teamId}/online-products/{productId}/availabilities/
Storing your ID
It might be useful to store your own id on each resource you manage, especially if you want to synchronize your
system with Yoplanning.
For example, in case of synchronization problems on your side, these ids allow you to recover resources even
if you lost the Yoplanning ID.
Therefore, you can store your ID in the external_reference fields in many resources (client, order,
session_group, session, payment, ...)
This is not mandatory at all.
When you create an object or a sub-object (the main_client of a new order for exemple), you should not give
the "id" property. Yoplanning will ignore that and create its own ID (that you will get in the response).
For instance, if the client already exist in the database, Yoplanning will try to reuse it instead of adding
a duplicate.
Filtering
For some endpoints, you can filter the results by adding query parameters to the request.
The following request will give you only the clients with first name "Norbert".
https://yoplanning.pro/api/v3.1/teams/5a90332e-568f-4980-9859-88a984844a4d/clients?offset=20&limit=2&first_name=Norbert
For date and numeric fields, you can filter using __lt (lower than), __gt (greater than), __lte (lower
than or equal), __gte (greater than or equal).
The following request will give you only the clients with birth date greater than 1995-01-25.
https://yoplanning.pro/api/v3.1/teams/5a90332e-568f-4980-9859-88a984844a4d/clients?offset=20&limit=2&birth_date__gt=1995-01-25
Please see each endpoint documentation to know which fields can be used as filters (filters allowed).
Expand fields
For some endpoints, the resource object contains nested resources. For example, a session contains staff.
When you retrieve this resource (using a GET request), you will only retrieve the nested resources ids (here:
staff ids).
To avoid sending more requests, you can use the expand mechanism of the API to retrieve the full nested
object.
To do so, just append a query parameter in the url : "expand=" with the name of the nested resource you
want to expand.
/api/v3.1/teams/5a90332e-568f-4980-9859-88a984844a4d/payments/c21010f4-0e68-435b-b076-480087a49db1?expand=operator
You can use a dot notation to go deeper in the object.
/api/v3.1/teams/5a90332e-568f-4980-9859-88a984844a4d/orders?expand=items.voucher.product
You can also expand several nested objects using a comma notation
/api/v3.1/teams/5a90332e-568f-4980-9859-88a984844a4d/orders?expand=items.voucher.product,payments.operator
Please refer to each endpoint documentation to know which field is expandable.
Note : When you retrieve an instance using a GET request on an instance endpoint, some nested objects may be
expanded by default.
This forced expand is sometimes necessary in order to allow you to provide this JSON in a PUT request.
Important : If you want to retrieve a resource in order to update it with a PUT request, you MUST
NOT USE the expand mechanism.
Webhooks
Webhooks are also called "web callback" or "HTTP push API". It is a notification system.
Rather than asking the API every few minutes whether something changed, you subscribe to an event (for example,
each time an order is updated) and we send a POST request to your own url when it happens. You then read only what
changed.
Webhooks are the way to keep your system in sync with orders and clients : the
last_update field and
filters are
deprecated, they do not move on every change (a confirmation, a cancellation, a
payment...).
Getting access
To receive webhooks on orders, please contact our support : we give you webhook access on orders and set up your
subscription. A subscription is made of :
- the team you want to be notified for ;
- the resource :
order, client, session_group,
product, team_member or unavailability ;
- the event :
created, updated or deleted, or all three ;
- your callback url ;
- the format (
webhook_version) : 1 or 2, see below.
On request, we also add headers of your choice to every call (a secret key, for example), so that your endpoint
can check that the call comes from us.
What you receive
When the event happens on the team, we send a POST request to your callback url, with a JSON body, a few seconds after
the change.
With
version 1, the body tells you which object changed and where to read it :
{
"team": { "id": "5a90332e-568f-4980-9859-88a984844a4d" },
"resource": {
"id": "8d23503e-041e-4180-98d1-641183bc5ead",
"type": "order"
},
"event": "updated",
"link": "https://yoplanning.pro/api/v3.1/teams/5a90332e-568f-4980-9859-88a984844a4d/orders/8d23503e-041e-4180-98d1-641183bc5ead"
}
team.id => the team on which the event occurred
resource.id => the object that has been created / updated / deleted
resource.type => the type of object (order / client / session_group, etc...)
event => the event fired
link => the url to use to retrieve the object
Send a GET request on
link to read the object as it is now. Don't forget to add your
authentication information when calling the API!
With
version 2, the body has no
link : it carries the object itself in
resource.data, as a GET on the API returns it (an order comes with
items.session_group.sessions
and
payments.operator expanded), so you have nothing to read back. On a
deleted event, rely on
resource.id only.
Events on orders
created : an order was created (in the office, online, through the API...). It can come again for
the same order, when an order on hold is confirmed for example : handle it by the order's id ;
updated : the order changed, for example cancelled, paid online, finalised, or its participants
changed ;
deleted : the order was deleted, or merged into another order (which receives an
updated).
Whatever the event, read the order's current state (its
booking_status, its payments...) rather than
deducing it from the event name : a cancelled order, for example, is
updated, not deleted.
Answering a webhook
- Answer with a
2xx status within 15 seconds, and process the notification afterwards.
- When your server does not answer within 15 seconds, or answers
500, 502,
503 or 504, we send it again, up to 3 times, within a few seconds.
- So the same notification can arrive twice, and notifications are not guaranteed to arrive in the order of the
changes : read the object's current state rather than applying the notifications one after the other.
- The changes you make yourself through the API, with your own token, are not notified back to you.
- A notification can still be lost (your server down for a while, for example) : once a night, re-read the
period you care about to reconcile (see Best practices).
Important : when you receive a webhook, read the object and update your system, but never send it
back to us with a PUT request : it could lead to an infinite loop.
Errors
Yoplanning uses HTTP response status codes to indicate the success or failure of your API requests.
In general, there are three status code ranges you can expect:
- 2xx success status codes confirm that your request worked as expected
- 4xx error status codes indicate an error because of the information provided (e.g., a
required parameter was omitted)
- 5xx error status codes are rare and indicate an error with Yoplanning’s servers. You can
still get this error in some rare case if the data you provided is not valid
4xx errors include some data to help you identify what was the problem. Here is an example:
{"price":["This field is required."]}
Keys: The name of the fields that have wrong value.
Values: A list of string describing the errors corresponding to this field.
Best practices
Every request you send is work on our side and counts against your limits (see Throttling). Most integrations
only need a small part of what they request: these rules keep your calls fast and far from the limits.
Always send a date window
A list without date filters reads the team's whole history, years of data, on every page. Send a lower
and an upper bound: a lower bound alone still returns everything planned after it.
Bad :
https://yoplanning.pro/api/v3.1/teams/{teamId}/session-groups/?offset=0&limit=100
Good :
https://yoplanning.pro/api/v3.1/teams/{teamId}/session-groups/?start_date__gte=2026-12-01&start_date__lte=2026-12-31
- Each endpoint lists the filters it accepts. A filter or a suffix that is not listed is ignored, and
you get the whole list :
/api/v3.1/teams/{teamId}/orders/ and
/api/v3.1/teams/{teamId}/unavailabilities/ take __gt / __lt, not
__gte / __lte.
- To get what overlaps a window, use
start_date__lt (the end of the window) with
end_date__gt (its start).
- Send one date per filter, like
2026-12-01 or 2026-12-01T08:00:00, never two dates in
the same value.
Paginate a window, not the whole history
Each page runs the query again and skips every row before
offset :
offset=15000 makes us read
15,000 rows to send you 100. To read a long period, cut it into windows (a month, a week) and paginate inside each
one by following
next.
Bad :
https://yoplanning.pro/api/v3.1/teams/{teamId}/orders/?offset=21500&limit=100
Good :
https://yoplanning.pro/api/v3.1/teams/{teamId}/orders/?creation_date__gt=2026-10-01&creation_date__lt=2026-11-01&limit=100,
then the
next url of each page, until it is null.
Keep the default page size
The default
limit is 100. Bigger pages are slower to build and to send, and a limit above the maximum is
lowered, not refused : always follow
next rather than computing the offsets from the limit you asked for.
Bad :
?limit=1000000
Good :
?limit=100, then
next.
Use webhooks to follow changes
Re-reading the same data every few minutes sends you the same objects again and again. Rather than asking us whether
something changed, let us tell you : with webhooks, we notify you when an order or a client is created, updated or
deleted, and you read only that object (see Webhooks). To get webhook access on orders, contact our support.
last_update and its filters (last_update__gt, last_update__lt) on
orders, clients and staff fees are deprecated : this date does not move on every change (a
confirmation, a cancellation, a payment...), so a synchronisation built on it misses changes without telling
you.
- On online products and availabilities,
last_modified__gt still asks for what changed since your
last run : use as the bound the highest last_modified you received (in UTC), not your own
clock.
- Once a night, re-read the period you care about to reconcile, in case a notification was lost.
Bad : every 5 minutes, all the orders of the team, or
orders/?last_update__gt=2026-10-22T09:15:03.
Good : webhooks on orders, plus a nightly
https://yoplanning.pro/api/v3.1/teams/{teamId}/orders/?creation_date__gt=2026-10-01&creation_date__lt=2026-11-01
One list with expand, not one request per object
Fetching objects one by one in a loop, often in parallel, is what reaches the limit of concurrent requests. One list
request returns up to 100 objects, and
expand brings their nested objects with them (see Expand fields).
Bad : a request to
/api/v3.1/teams/{teamId}/orders/{id}/ for each order.
Good :
https://yoplanning.pro/api/v3.1/teams/{teamId}/orders/?creation_date__gt=2026-10-01&creation_date__lt=2026-11-01&expand=items.session_group,payments
Cache what rarely changes
Products (with their places) and categories change a few times a season. Read them once, keep them, and refresh them
once a day or when a
product webhook arrives.
Bad : reading
/api/v3.1/teams/{teamId}/online-products/ before each booking.
Good : a copy on your side, refreshed daily, also for
/api/v3.1/categories/.
Back off on 429
A 429 answer means you reached a limit (see Throttling). Wait the number of seconds given in its
Retry-After header before sending again, and never retry right away in a loop : it only keeps you over
the limit.
Bad : retrying a refused request immediately, or in parallel.
Good : a few workers at most, and a pause of
Retry-After seconds after a 429.
Run each job once
The same synchronisation running on two machines doubles the requests, and both share the limits of your token : they
reach them twice as fast. Run each job from a single scheduler, and do not start a run while the previous one is
still going.
Identify yourself
Send a
User-Agent header naming your company and a contact, not the default of your HTTP library
(
node,
python-requests,
curl) : when an integration loads the API, it is how we
can reach you.
User-Agent: MyCompany-Sync/2.3 (tech@mycompany.com)
Tutorial: Sell activities online
Introduction
This tutorial will show you how to build a booking engine based on the Yoplanning API.
If you want to be able to sell Yoplanning activities in your system, you are in the right place to start.
We assume that you have read the API documentation and that you already have your API token.
We will cover the following steps involved in the workflow :
- Retrieve your teams data
- Get the list of products
- Get availabilities for a specific product
- (Optional) Retrieve available options (resources)
- (Optional) Check everything is still available
- (Optional) Hold the places while your client pays
- (Optional) Handle the payment via Payment Manager
- Place the order (or confirm the one on hold)
Retrieve your teams data
First, you need to retrieve your teams IDs to be able to make use of the API
There are 2 very important information you need to retrieve
and store in your system :
- team_id : You will need this id for pretty much every request on the API
- vendor_id : You will need this id only if you want to use the Payment Manager for the payment - see (Optional) Handle the payment via Payment Manager
You can retrieve all your teams at once using the following endpoint :
/api/v3.1/teams/
This step should not be done for every payment !
You should do this only once and
store the team_id and vendor_id in your system.
Depending on your needs, you can also skip this step and simply contact the support to ask for your team_id and vendor_id.
Get the list of products
First of all, you want to know what you can sell. To do so, you will have to retrieve the list of products that can be sold online for your team.
Here is the endpoint you are going to use : /api/v3.1/teams/{teamId}/online-products/
Example of call using Curl :
curl https://yoplanning.pro/api/v3.1/teams/5a90332e-568f-4980-9859-88a984844a4d/online-products -H 'Authorization: Token 4504c2cb0d87a93106d4de029f407c86149f2ada'
Obviously, you have to replace the ID by the ID of your team and the token by the token we gave to you.
This will return a list of products (please see Pagination, Filter and expand fields in the documentation)
Each product contains a title, a description and other translatable content. You can retrieve this content in several different languages (the same ones available in the Yoplanning interface).
To do so, just give an additional query parameter with the language code (ISO 2).
Example : curl https://yoplanning.pro/api/v3.1/teams/5a90332e-568f-4980-9859-88a984844a4d/online-products?lang=fr -H 'Authorization: Token 4504c2cb0d87a93106d4de029f407c86149f2ada'
You can also filter by category. If you want to do so please refer to /api/v3.1/categories/.
You can now display the list of products.
Get availabilities for a product
The next step is to retrieve the availabilities (sessions) for a given product.
Basically, when the user will click on a product, you are going to display the availabilities for this product.
Here is the endpoint you are going to use : /api/v3.1/teams/{teamId}/online-products/{productId}/availabilities/
Example of call using Curl :
curl https://yoplanning.pro/api/v3.1/teams/5a90332e-568f-4980-9859-88a984844a4d/online-products/f16ed6c6-e972-4232-b452-ecd393e61642/availabilities -H 'Authorization: Token 4504c2cb0d87a93106d4de029f407c86149f2ada'
Obviously, you have to replace the team ID(5a90332e-568f-4980-9859-88a984844a4d), product ID(f16ed6c6-e972-4232-b452-ecd393e61642) and token.
You can filter by date, price, number of tickets, staff (instructor) etc...
For each availability, you will get the associated prices. The "standard price" is the default price. This is the price everyone will get without anything else specified.
The "other prices" are a set of prices that can be chosen for each participant. For example : child price, member price, etc...
You can now display the availabilities.
Product priced per session (price_per_participant false on the product) : one session holds at most max_participants people, while nb_tickets may add up several sessions.
Do not let one booking go over max_participants, or split it into several sessions as explained in "Place the order".
(Optional) Retrieve available options (resources)
In case the team is selling/renting equipments along with sessions, you might want to present them to the purchaser after they
chose an availability from the list retrieved in the previous step ("Get availabilities for a product").
To do so, you will need to retrieve the details of the desired availability using the following endpoint : /api/v3.1/teams/{teamId}/availability-details/{id}/
Replace {id} with the "session_group" id you retrieved from the previous call ("Get availabilities for a product").
This endpoint will give you all you need about the different options available. The current stock is taken into account so only the available resources will be in the response.
For more information about what is an option, how you should display them and what's the json structure,
please refer to the API documentation.
Here is an example using CURL :
curl https://yoplanning.pro/api/v3.1/teams/5a90332e-568f-4980-9859-88a984844a4d/availability-details/374facd3-df50-4295-a0ff-7b90eb6a26e2/ -H 'Authorization: Token 4504c2cb0d87a93106d4de029f407c86149f2ada'
You can now display each option as a dropdown. When you will finally create the order, you will use the "id" retrieved here to fill the "resource" field.
And you will use the "session_group" id to fill the "resource_target_group". Please take a look at the last step "Place the order" and to the API documentation for more information.
Resource priced as a percentage (price_percent not null, a cancellation insurance for instance) : its price is that percentage of the whole session-group price.
For a product priced per session (price_per_participant false), the session price is paid once by all the participants, so compute the price of the resource on the share of one participant :
310 for 4 participants with a 3.75 % insurance = 3.75 % of 77.50 = 2.91 per insured participant, not 11.63. Same with a price variation (child rate...) : apply price_percent to the price of the participant's line.
(Optional) Check everything is still available
If you have a lot of customers making bookings at the same time, you might want to increase the robustness of the system to avoid overbooking.
To do so, you can simply check that the cart content is still available before checkout.
Here is the endpoint we are going to use : /api/v3.1/teams/{teamId}/order-validation
You just need to send the exact same JSON you would send to place an order (see below : "Place the order").
The only difference is the URL.
Here is an example using CURL :
curl -H "Content-Type: application/json" -X POST -d '{"external_reference" : "956", "items" : [{"session_group" : "53071a97-0c2d-4973-89f5-cafd10665b3b", "price" : {"amount" : 51.25}, "client" : {"id" : "71ea849f-226c-4302-a433-528179634aa7", "first_name" : "John", "last_name" : "Doe", "email" : "john.doe@gmail.com"}}], "payments" : [{"amount" : 51.25, "client" : {"first_name" : "Framold", "last_name" : "Doe", "email" : "john.doe@gmail.com"}}]}' https://yoplanning.pro/api/v3.1/teams/5a90332e-568f-4980-9859-88a984844a4d/order-validation -H 'Authorization: Token 4504c2cb0d87a93106d4de029f407c86149f2ada'
If everything is still available (most of the time), you should receive this answer :
{ "success": true }
If some items are not available anymore, you should receive an answer similar to this one :
{
"success": false,
"unavailable_items": {
"group_ids": ["2265cac0-cba5-46eb-8095-b89f93e7473f"],
"dry_resource_ids": [],
"voucher_ids": [],
"resources": [],
"deleted_group_ids": []
}
}
if success is true, you can proceed to the next step.
A session-group is also listed in group_ids when, for a product priced per session, one cart_group_id holds more participants than one session can (see "Place the order").
A session-group listed in deleted_group_ids no longer exists : it was deleted since you read the catalogue (for a private product, the session-group of an order is deleted with that order). Read the catalogue again and use the session-group ids it gives, never an id kept from an earlier order.
An order placed on hold (see below) runs this check by itself : the POST answers 400 with the same unavailable_items when something is not available anymore.
(Optional) Hold the places while your client pays
By default, you place the order
once your client has paid (see the last step : "Place the order").
While your client is paying, someone else can book the last places : checking the cart ("Check everything is still available") reduces that risk, it does not remove it.
If you want the places to be kept for your client,
hold them instead : create the order
on hold before the payment, then confirm it once your client has paid.
- Without a hold : one call, after the payment. The order is created confirmed.
- With a hold : the order is created on hold before the payment and keeps the places for a limited time, then you confirm it (or cancel it) with a second call.
1. Create the order on hold
Use the same endpoint and the same JSON as "Place the order" :
/api/v3.1/teams/{teamId}/orders/, with 2 more fields :
"booking_status": "ON_HOLD"
"expiration_minutes" (optional) : how long the places are held, from 15 to 120 minutes (15 when omitted)
An order on hold
never carries a payment : leave
payments empty, a payment is refused (400) until the order is confirmed.
Example of call using Curl :
curl -H "Content-Type: application/json" -X POST -d '{"external_reference" : "956", "booking_status" : "ON_HOLD", "expiration_minutes" : 30, "items" : [{"session_group" : "4f655815-58d9-4c7c-b1c9-2ea152073371", "price" : {"amount" : 51.25}, "client" : {"id" : "71ea849f-226c-4302-a433-528179634aa7", "first_name" : "John", "last_name" : "Doe", "email" : "john.doe@gmail.com"}}], "payments" : []}' https://yoplanning.pro/api/v3.1/teams/33ab5a4e-1e90-40fe-95a3-32a61bab0f45/orders -H 'Authorization: Token xxx'
The places are held right away : they are no longer available to other bookings. No booking email is sent to your client yet.
If something is not available anymore, the order is not created : the POST answers 400 with the same
unavailable_items as "Check everything is still available". Read the catalogue again and use the session-group ids it gives.
The answer gives you the order
id (store it, you need it for the next calls), its
booking_status (
ON_HOLD) and its
expires_at (in the team timezone, like the other dates).
2. (Optional) Give your client more time
If the payment takes longer than expected, extend the hold before it expires :
/api/v3.1/teams/{teamId}/orders/{orderId}/extend
The minutes you give are added to
expires_at.
curl -H "Content-Type: application/json" -X POST -d '{"expiration_minutes" : 10}' https://yoplanning.pro/api/v3.1/teams/33ab5a4e-1e90-40fe-95a3-32a61bab0f45/orders/0b4f2d6e-3c1a-4e8b-9f0d-7a2c5e1b8d34/extend -H 'Authorization: Token xxx'
3. Confirm the order once your client has paid
No body is needed :
/api/v3.1/teams/{teamId}/orders/{orderId}/confirm
curl -X POST https://yoplanning.pro/api/v3.1/teams/33ab5a4e-1e90-40fe-95a3-32a61bab0f45/orders/0b4f2d6e-3c1a-4e8b-9f0d-7a2c5e1b8d34/confirm -H 'Authorization: Token xxx'
The order becomes
CONFIRMED : from now on it behaves exactly like an order placed without a hold (the booking email is sent to your client, the team is notified, etc...).
Then, and only then, record your client's payment on it :
/api/v3.1/teams/{teamId}/orders/{orderId}/payments/
Do not place a new order : the order you confirmed already holds the places.
If your client does not pay
Cancel the order to give the places back right away :
/api/v3.1/teams/{teamId}/orders/{orderId}/cancel (no body, your token needs the delete right).
curl -X POST https://yoplanning.pro/api/v3.1/teams/33ab5a4e-1e90-40fe-95a3-32a61bab0f45/orders/0b4f2d6e-3c1a-4e8b-9f0d-7a2c5e1b8d34/cancel -H 'Authorization: Token xxx'
Or simply let it expire : once
expires_at is past, the order is deleted automatically and its places are given back.
An expired order can no longer be confirmed (the call answers 400) : if the payment arrives too late, refund your client, or check the availabilities and place a new order.
Good to know
- The list of orders (
/api/v3.1/teams/{teamId}/orders/) returns confirmed orders only by default : add ?booking_status=ON_HOLD, CANCELLED or all to see the others.
- The
booking_status of an order only changes through confirm and cancel.
expires_at is only set through expiration_minutes and extend : a value you send yourself is ignored.
- Confirm and extend need the create right on the orders, cancel needs the delete right.
(Optional) Create a payment via Payment Manager
Before getting started, make you sure you have stored the
vendor_id in your system (see step 1 : Retrieve your teams data)
Once the cart is ready, the next step is to checkout and proceed to payment.
To handle your client payment, you have 2 options : you can use your own payment solution or you can use the Payment Manager provided by Yoplanning.
If you choose to use the Payment Manager, here is the workflow you need to implement :
- Create a payment and retrieve the payment link
- Redirect your client on the payment link
- handle the IPN (Instant Payment Notification)
First, you need to create a payment
To do so, you will use another API and thus another base URL :
https://payment.yoplanning.pro/api/
The endpoint we are going to use here is :
https://payment.yoplanning.pro/api/create-payment
We are going to use the
POST HTTP method because we want to create a new payment.
You will need to setup a callback url on your system. This url must be reachable without authentication and must accept POST requests.
Once the payment is done, you will receive an IPN on this url.
Here is the JSON data you can provide :
{
"order_id": (required) This is the order id in your system
"vendor_id":(required) This is your vendor id
"price": (required)
"currency": (required)
"callback_url": (required) Once the payment is done, you will receive an IPN on this url
"redirection_url": When the payment is done, the user will be redirected to this URL
"cancel_url" : The user will be redirected on this url if the payment is cancelled
"payer_email": used to pre-fill the payment form
"cardholder_first_name": used to pre-fill the payment form
"cardholder_last_name": used to pre-fill the payment form
"billing_address_line1":
"billing_address_line2":
"billing_address_city":
"billing_address_postal_code":
"billing_address_country":
"billing_address_state":
}
Example of call using Curl :
curl -H "Content-Type: application/json" -X POST -d '{"order_id": "RFGYJ52","vendor_id": "a3dfc41e-5869-42d1-a8ec-a1d0280dcbf8","price": 59.84,"currency": "EUR","callback_url": "https://mysite.com/payment-callback","redirection_url": "https://mysite.com/payment-redirection","cancel_url" : "https://mysite.com/payment-cancelled","payer_email": "yousef.dupond@gmail.com","cardholder_first_name": "Yousef","cardholder_last_name": "Dupond","billing_address_line1": "7 route du chef-lieu","billing_address_city": "Annecy","billing_address_postal_code": "74000", "billing_address_country": "FR"}' https://payment.yoplanning.pro/api/create-payment -H 'Authorization: Token 3ef270c8a2ed6e15207ba8b0dab59ed7eb872ae8'
Be careful : the header "Content-Type: application/json" is important.
Here is an example of the answer you should get :
{
"payment_solution": "Stripe",
"vakario_fee": "1.40",
"customer_id": null,
"success": true,
"payment_id": "18135b43-f61b-4041-b89c-98c7086e8f68",
"payment_url": "https://payment.yoplanning.pro/pay/3be2d0a0-f9a2-4fb0-aba2-bc3301c399c9"
}
if success is true, you can proceed to the next step.
Redirect the client on the payment page
All you need to do is to redirect the client on the payment_url received in the previous step.
Handle the IPN
When the payment is done, you will receive an Instant Payment Notification (IPN) on the callback_url you provided when you created the payment.
The callback url must be accessible by POST without authentication.
The IPN is sent as form fields (
application/x-www-form-urlencoded), not as JSON : the booleans arrive as the strings
True /
False, and a field without a value is left out.
id : the id of the paid payment. The same IPN can be sent again : use it to recognise an IPN you have already handled.
order_id : the order_id you sent when you created the payment.
payed : True when the payment is done.
failed : True when the payment failed.
amount : the amount to record for this payment. When the fee is added on top of your price (vakario_fee_included is False), it is already deducted.
payment_solution : the payment provider used (Stripe, Adyen, ANCV Passerelle...).
date : the payment date, UTC, YYYY-MM-DD HH:MM:SS.
ancv_complement_of : only on the Chèque-Vacances part of a split payment (see below).
Here is an example of the IPN you should receive :
id=3d23247e-6115-4f2a-9258-f8071c0d0e07
order_id=E816E3DA
payed=True
failed=False
amount=59.84
payment_solution=Stripe
vakario_fee=1.50
vakario_fee_included=True
date=2026-11-02 21:08:03
If payed is True, you can proceed to the next step : place the order with the IPN
amount as the payment amount, and send the IPN
id as the
payment_manager_id of the payment.
Payment split between card and Chèque-Vacances (ANCV Connect)
When the team accepts both card payments and Chèque-Vacances Connect, the payment page lets your client put part of the amount on their Chèque-Vacances and pay the rest by card.
The client pays the card part first, then goes straight to the Chèque-Vacances part.
You then receive
two IPNs for one payment :
- both carry the order_id you sent
- each has its own
id and its own amount : the card share, then the Chèque-Vacances share
- the Chèque-Vacances IPN also carries
ancv_complement_of, which is the id of the card IPN
Example for a 120 EUR payment, 40 EUR of it in Chèque-Vacances :
1st IPN (card) : id=a1b2c3d4-..., order_id=E816E3DA, payment_solution=Adyen, amount=80.00
2nd IPN (Chèque-Vacances) : id=e5f6a7b8-..., order_id=E816E3DA, payment_solution=ANCV Passerelle, amount=40.00, ancv_complement_of=a1b2c3d4-...
For each IPN where payed is True :
- Ignore an
id you have already handled.
- Record the IPN
amount as the payment amount, never your cart total.
- If you have not placed the Yoplanning order for this order_id yet, place it with this payment :
/api/v3.1/teams/{teamId}/orders/.
Otherwise add the payment to that order : /api/v3.1/teams/{teamId}/orders/{orderId}/payments/. Never place a second order.
- The card IPN usually arrives first, but the Chèque-Vacances one can come first : the previous rule covers both. Handle the IPNs of one order_id one at a time, so that two orders are never placed in parallel.
- If your client abandons the Chèque-Vacances step, the second IPN never comes : the order stays confirmed with a balance due, which the team collects.
If you held the places (see "(Optional) Hold the places while your client pays"), confirm the order on the first IPN, then add each payment to it.
Place the order
Once the client has paid his cart, you will basically want to place an order to Yoplanning. When you place an order, the team planning is updated, the order & participants are displayed in Yoplanning, the staffs are notified, the availabilities are updated for the future bookings, etc...
Here is the endpoint you are going to use : /api/v3.1/teams/{teamId}/orders/
For the previous 2 calls, we used the HTTP method GET (because we wanted to retrieve data).
This time, we are going to use POST method because we want to add a new order.
Example of call using Curl :
curl -H "Content-Type: application/json" -X POST -d '{"external_reference" : "956", "items" : [{"session_group" : "4f655815-58d9-4c7c-b1c9-2ea152073371", "price" : {"amount" : 51.25}, "client" : {"id" : "71ea849f-226c-4302-a433-528179634aa7", "first_name" : "John", "last_name" : "Doe", "email" : "john.doe@gmail.com"}}], "payments" : [{"amount" : 51.25, "client" : {"first_name" : "Framold", "last_name" : "Doe", "email" : "john.doe@gmail.com"}}]}' https://yoplanning.pro/api/v3.1/teams/5a90332e-568f-4980-9859-88a984844a4d/orders -H 'Authorization: Token 4504c2cb0d87a93106d4de029f407c86149f2ada'
Obviously, you have to replace the team ID(5a90332e-568f-4980-9859-88a984844a4d) and token.
Be careful : the header "Content-Type: application/json" is important.
The session-group correspond to the session-group of an availability.
If your client paid through the Payment Manager, the payment amount is the IPN amount : it is lower than the order total when your client split the payment between card and Chèque-Vacances (see "Handle the IPN").
Product priced per session (price_per_participant false on the product) : the items sharing a cart_group_id (an integer, unique within the order) are ONE session sold, whatever their quantity, and the items without cart_group_id are one group too.
A group must not hold more participants than the max_participants of the availability : for more people, send one cart_group_id per session.
For example, 4 people with max_participants 2 = 2 items of quantity 2, with "cart_group_id" : 1 and "cart_group_id" : 2, each group being one session sold.
order-validation refuses a bigger group, placing the order does not : the session would be overbooked.
If you held the places before the payment (see "(Optional) Hold the places while your client pays"), do not place a new order : confirm the one on hold instead.