Вашият софтуер, вашите резервации.
API-то на CocoBook дава на мениджър на канали, уиджет за резервации, счетоводен експорт или ваш собствен скрипт достъп до резервациите, наличността и цените на един имот. JSON на вход и изход, API ключове с права, безопасни повторни опити, OpenAPI описание.
Бърз старт
В CocoBook отворете Настройки → Разработчици и създайте ключ за имота, който искате да свържете. Изберете нужните му права; ключът се показва само веднъж. След това проверете дали работи:
Заявка
curl https://api.cocobook.app/v1/me \
-H "Authorization: Bearer cb_live_3fK9aBxQ7mNp2vLw8rTz4hJd6kYs1eCgU5oWnR0i"
Отговор · 200
{
"key": {
"id": "a1c3e5f7-9b2d-4e6f-8a0c-2d4f6b8a0c1e",
"name": "Channel manager",
"prefix": "cb_live_3fK9aB",
"scopes": ["bookings:read", "bookings:write"],
"created_at": "2026-10-14T09:12:00Z",
"expires_at": "2027-10-14T09:12:00Z"
},
"property": {
"id": "7d2f8a1c-9e4b-4d6a-8c3f-1b5e7a9d2c60",
"name": "Sea House",
"currency": "EUR",
"location": {
"country": "BG",
"country_name": "Bulgaria",
"region": "Burgas",
"town": "Sozopol",
"label": "Sozopol, Bulgaria"
},
"image_url": "https://cocobook.app/static/uploads/properties/7d2f8a1c.jpg",
"created_at": "2026-03-02T15:40:11Z"
}
}
expires_at присъства само ако ключът има срок на валидност, а image_url — само ако имотът има снимка. Същият обект property се връща и от GET /property.
Базов адрес: https://api.cocobook.app/v1. Машинно четимото описание е на /v1/openapi.json — заредете го в Postman, Insomnia или генератор на SDK.
Удостоверяване и права
Всяка заявка носи ключа в хедъра Authorization като bearer токен (работи и X-API-Key: cb_live_…). Ключът принадлежи на един имот: няма параметър за имот, а всичко, което принадлежи на друг имот, просто връща 404. Само собственици и администратори могат да създават и отменят ключове; отмяната важи веднага.
| Право (scope) | Позволява |
|---|---|
bookings:read | Списък и четене на резервации, наличност, оферти за цена |
bookings:write | Създаване, промяна и отказ на резервации; създаване на блокирания |
property:read | Четене на профила на имота и стаите му |
property:write | Промяна на профила на имота (име, валута, местоположение) |
Какво получавате, когато ключът липсва, е грешен или няма нужното право:
Без ключ · 401
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="cocobook-api"
{"error": {"code": "missing_api_key", "message": "Send the API key as 'Authorization: Bearer cb_live_…'."}}
Непознат, отменен или изтекъл ключ · 401
{"error": {"code": "invalid_api_key", "message": "The API key is not valid."}}
Ключ без нужното право · 403
{"error": {"code": "insufficient_scope", "message": "This key does not have the 'bookings:write' scope."}}
Отговорът 401 нарочно е еднакъв за отменен ключ и за ключ, който никога не е съществувал. GET /me не изисква никакво право, затова е подходящата заявка за бутон „Тест на връзката“.
Пазете ключа като парола: ползвайте го от сървъра си, никога от уеб страница или мобилно приложение, и давайте на всяка интеграция собствен ключ с минималните права, които ѝ трябват.
Общи правила
- JSON, UTF-8. Изпращайте
Content-Type: application/json. Непознатите полета се отхвърлят, така че правописните грешки излизат като400. - Идентификаторите са непрозрачни низове. Датите са във формат
YYYY-MM-DD;check_outи всякоtoса невключителни (1–4 юни са три нощувки). Времевите отпечатъци са RFC 3339 в UTC. - Списъците идват като
{"data": […], "page", "per_page", "total"};per_pageе до 200. - Грешките идват като
{"error": {"code", "message", "details"}}.detailsпосочва проблемните полета. - Незадължителните текстови полета (
guest.email,guest.phone,notes,image_url,expires_at) се пропускат в отговорите, когато са празни, вместо да се изпращат катоnull. - Нищо не се кешира (
Cache-Control: no-store). Версията е в пътя; в рамките наv1полета само се добавят.
Всеки удостоверен отговор носи хедърите за лимита на заявки, независимо от статуса:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 37
Ендпойнти
| Ендпойнт | Право | Какво прави |
|---|---|---|
GET/me | — | Ключът и неговият имот |
GET/property | property:read | Профил |
PATCH/property | property:write | Промяна на име, валута, местоположение |
GET/units, /units/{id} | property:read | Стаи с капацитет, базова цена, закуска |
GET/availability | bookings:read | Заети периоди по стаи в даден интервал |
GET/quote | bookings:read | Цена за престой |
GET/bookings, /bookings/{id} | bookings:read | Списък с филтри, четене на една |
POST/bookings | bookings:write | Създаване на резервация или блокиране |
PATCH/bookings/{id} | bookings:write | Промяна на произволни полета |
DELETE/bookings/{id} | bookings:write | Отказ (също POST …/cancel) |
Резервации
Примерите по-долу проследяват една резервация през целия ѝ живот: създадена за престой през юли, прочетена, удължена с една нощувка и накрая отказана. Всички използват стаята „Sea view“ от Имот и стаи.
Създаване
Заявка
curl -X POST https://api.cocobook.app/v1/bookings \
-H "Authorization: Bearer cb_live_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-8812" \
-d '{
"unit_id": "b0e562a3-7c1e-4f0a-9d2b-5e8f1a6c3d47",
"check_in": "2026-07-03",
"check_out": "2026-07-06",
"guest": {"name": "Ana Petrova", "email": "ana@example.com", "nationality": "BG"},
"source": "booking_com",
"breakfast_guests": 2,
"deposit": 100
}'
Задължителни: unit_id, check_in, check_out, guest.name. status по подразбиране е confirmed (или изпратете pending; резервациите, които екипът приема в CocoBook без депозит, се връщат като deposit_pending, което държи датите като confirmed); source по подразбиране е direct и е едно от direct, airbnb, booking_com, agoda, referral. Престоят е най-много 365 нощувки. Ако пропуснете price, цената се изчислява от сезоните на имота плюс закуската, точно както би я изчислил /quote. Отговорът е 201 с резервацията:
Отговор · 201
{
"id": "e394b1d0-2a6f-4c8e-b7d3-91f0a5c2e468",
"code": "B-4762",
"unit": {"id": "b0e562a3-7c1e-4f0a-9d2b-5e8f1a6c3d47", "name": "Sea view"},
"guest": {"name": "Ana Petrova", "email": "ana@example.com", "nationality": "BG"},
"check_in": "2026-07-03",
"check_out": "2026-07-06",
"nights": 3,
"status": "confirmed",
"source": "booking_com",
"price": 330,
"deposit": 100,
"currency": "EUR",
"breakfast": {"guests": 2, "price": 30},
"created_at": "2026-10-14T09:14:02Z",
"updated_at": "2026-10-14T09:14:02Z"
}
price е общата сума за престоя с включена закуска (тук 300 за стаята плюс 30 за закуска); breakfast.price е само сумата за закуската. code е краткият номер, който екипът вижда в CocoBook.
Ако изпратите поле, което API-то не познава, или невалиден JSON, тялото се отхвърля изцяло:
Отговор · 400
{"error": {"code": "invalid_json", "message": "json: unknown field \"check_int\""}}
При правилно оформено тяло с грешни стойности се посочват всички проблемни полета наведнъж, така че една заявка стига, за да поправите формуляр:
Отговор · 400
{
"error": {
"code": "validation_error",
"message": "One or more fields are invalid.",
"details": {
"check_out": "must be after check_in",
"guest.email": "must be a valid email address",
"source": "must be direct, airbnb, booking_com, agoda or referral"
}
}
}
Стаята трябва да е свободна за целия престой. Интерфейсът на CocoBook позволява на екипа да създава застъпващи се резервации и ги маркира като конфликти; API-то вместо това ги отказва: 409 unit_unavailable, когато се застъпва друга активна резервация, и 409 unit_blocked, когато се застъпва блокиране. details.conflicting_booking_ids посочва кои:
Отговор · 409
{
"error": {
"code": "unit_unavailable",
"message": "The unit already has a booking overlapping these dates.",
"details": {"conflicting_booking_ids": ["0b1c7d9e-5f3a-4b2c-8e6d-7a9f1c3e5b20"]}
}
}
Безопасни повторни опити
Изпратете хедър Idempotency-Key (произволен низ, уникален за опита, до 255 знака) и повторен опит след изтекло време ще върне резервацията, създадена първия път, вместо дубликат. Стойностите се помнят 24 часа за всеки API ключ. Повторението връща 200, а не 201, и е маркирано с хедър:
Същата заявка отново · 200
HTTP/1.1 200 OK
Idempotent-Replayed: true
{"id": "e394b1d0-2a6f-4c8e-b7d3-91f0a5c2e468", "code": "B-4762", …}
Сравнява се само ключът, не и тялото, затова никога не използвайте повторно един ключ за различна поръчка.
Блокирания
Задайте status на cleaning, owner или blocked, за да затворите дати без гост. Тогава guest.name не е задължително, а блокирането никога няма цена, каквото и да изпратите:
Заявка
POST /v1/bookings
{"unit_id": "b0e562a3-7c1e-4f0a-9d2b-5e8f1a6c3d47", "check_in": "2026-07-06", "check_out": "2026-07-07", "status": "cleaning"}
Отговор · 201
{
"id": "c58a2f61-0d4e-4b9a-a3c7-6e1f8b2d9a05",
"code": "BL-2210",
"unit": {"id": "b0e562a3-7c1e-4f0a-9d2b-5e8f1a6c3d47", "name": "Sea view"},
"guest": {},
"check_in": "2026-07-06",
"check_out": "2026-07-07",
"nights": 1,
"status": "cleaning",
"source": "direct",
"price": 0,
"deposit": 0,
"currency": "EUR",
"breakfast": {"guests": 0, "price": 0},
"created_at": "2026-10-14T09:15:30Z",
"updated_at": "2026-10-14T09:15:30Z"
}
Блокиранията получават код BL- и празен guest. Екипът ви вижда всяка промяна през API-то в известията на CocoBook, с името на ключа.
Четене на една
Заявка
curl https://api.cocobook.app/v1/bookings/e394b1d0-2a6f-4c8e-b7d3-91f0a5c2e468 \
-H "Authorization: Bearer cb_live_…"
Отговаря с 200 и същия обект на резервация като при създаване. Идентификатор от друг имот, правописна грешка или изтрит запис дават един и същ отговор:
Отговор · 404
{"error": {"code": "not_found", "message": "No booking with that id."}}
Списък и синхронизация
GET /v1/bookings?from=2026-07-01&to=2026-08-01 # резервации, застъпващи юли
GET /v1/bookings?status=confirmed,pending&unit_id=b0e5…
GET /v1/bookings?updated_since=2026-10-14T09:00:00Z # проверявайте това за промени
GET /v1/bookings?include_blocks=true&page=2&per_page=100
Отговор · 200
{
"data": [
{
"id": "e394b1d0-2a6f-4c8e-b7d3-91f0a5c2e468",
"code": "B-4762",
"unit": {"id": "b0e562a3-7c1e-4f0a-9d2b-5e8f1a6c3d47", "name": "Sea view"},
"guest": {"name": "Ana Petrova", "email": "ana@example.com", "nationality": "BG"},
"check_in": "2026-07-03", "check_out": "2026-07-06", "nights": 3,
"status": "confirmed", "source": "booking_com",
"price": 330, "deposit": 100, "currency": "EUR",
"breakfast": {"guests": 2, "price": 30},
"created_at": "2026-10-14T09:14:02Z", "updated_at": "2026-10-14T09:14:02Z"
},
{
"id": "f7a0c3e2-6b1d-4a8f-9c5e-3d2b7f1a8e94",
"code": "B-1183",
"unit": {"id": "4f7a9c21-3b8d-4e6a-a1c5-2d9e8b7f6a10", "name": "Garden studio"},
"guest": {"name": "Tom Weber", "phone": "+49 170 1234567", "nationality": "DE"},
"check_in": "2026-07-10", "check_out": "2026-07-17", "nights": 7,
"status": "pending", "source": "direct",
"price": 560, "deposit": 0, "currency": "EUR",
"breakfast": {"guests": 0, "price": 0},
"notes": "Arrives after 22:00",
"created_at": "2026-10-02T18:03:44Z", "updated_at": "2026-10-02T18:03:44Z"
}
],
"page": 1,
"per_page": 50,
"total": 2
}
Подредени по дата на настаняване. total брои всички съвпадения във всички страници; page започва от 1, а per_page по подразбиране е 50. from и to избират резервациите, които се застъпват с интервала, а не само тези, които започват в него. Блокиранията се пропускат, освен ако не е зададено include_blocks=true или status не ги посочва. Отказаните резервации остават достъпни със status: cancelled.
Резервация, която екипът е създал върху друга в CocoBook, се връща със status: conflict; можете да филтрирате по него, но API-то никога не създава такава.
За синхронизация запомнете кога сте проверили последно и поискайте всичко променено оттогава; отказ или редакция в CocoBook обновява updated_at, така че промяната се появява:
GET /v1/bookings?updated_since=2026-10-14T09:00:00Z&include_blocks=true&per_page=200
Промяна
PATCH приема същите полета като създаването; променят се само изпратените. Преместването на датите или стаята проверява отново за застъпвания (собствените дати на резервацията не се броят) и, ако не е изпратена price, преизчислява цената. Удължаване на престоя на Ана с една нощувка:
Заявка
curl -X PATCH https://api.cocobook.app/v1/bookings/e394b1d0-2a6f-4c8e-b7d3-91f0a5c2e468 \
-H "Authorization: Bearer cb_live_…" \
-H "Content-Type: application/json" \
-d '{"check_out": "2026-07-07", "notes": "late arrival"}'
Отговор · 200
{
"id": "e394b1d0-2a6f-4c8e-b7d3-91f0a5c2e468",
"code": "B-4762",
"unit": {"id": "b0e562a3-7c1e-4f0a-9d2b-5e8f1a6c3d47", "name": "Sea view"},
"guest": {"name": "Ana Petrova", "email": "ana@example.com", "nationality": "BG"},
"check_in": "2026-07-03",
"check_out": "2026-07-07",
"nights": 4,
"status": "confirmed",
"source": "booking_com",
"price": 430,
"deposit": 100,
"currency": "EUR",
"breakfast": {"guests": 2, "price": 40},
"notes": "late arrival",
"created_at": "2026-10-14T09:14:02Z",
"updated_at": "2026-10-14T11:02:19Z"
}
Ако блокирането за почистване от по-горе все още стоеше на 6–7 юли, отговорът щеше да е 409 unit_blocked. Изпратете "", за да изчистите guest.email, guest.phone или notes; за да запазите договорената цена при преместване на датите, изпратете я заедно с тях:
Заявка
PATCH /v1/bookings/{id}
{"check_in": "2026-07-04", "check_out": "2026-07-08", "price": 400, "guest": {"email": ""}}
Отказ
Заявка
curl -X DELETE https://api.cocobook.app/v1/bookings/e394b1d0-2a6f-4c8e-b7d3-91f0a5c2e468 \
-H "Authorization: Bearer cb_live_…"
Отговор · 200
{
"id": "e394b1d0-2a6f-4c8e-b7d3-91f0a5c2e468",
"code": "B-4762",
"status": "cancelled",
"check_in": "2026-07-03",
"check_out": "2026-07-07",
"price": 430,
"updated_at": "2026-10-15T08:30:00Z",
…
}
POST /v1/bookings/{id}/cancel и PATCH с {"status": "cancelled"} правят същото. Отказът запазва резервацията със status: cancelled и освобождава датите и паркомястото ѝ; повторен отказ е безопасен и отново връща 200.
Наличност и оферти
Наличност
Заявка
curl "https://api.cocobook.app/v1/availability?from=2026-07-01&to=2026-07-31&unit_id=b0e562a3-7c1e-4f0a-9d2b-5e8f1a6c3d47" \
-H "Authorization: Bearer cb_live_…"
Отговор · 200
{
"from": "2026-07-01",
"to": "2026-07-31",
"units": [
{
"unit": {"id": "b0e562a3-7c1e-4f0a-9d2b-5e8f1a6c3d47", "name": "Sea view"},
"busy": [
{"from": "2026-07-03", "to": "2026-07-06", "type": "booking", "status": "confirmed", "booking_id": "e394b1d0-2a6f-4c8e-b7d3-91f0a5c2e468"},
{"from": "2026-07-06", "to": "2026-07-07", "type": "block", "status": "cleaning"},
{"from": "2026-07-10", "to": "2026-07-14", "type": "external", "summary": "Airbnb (Not available)"}
]
}
]
}
Всичко, което не е свободно в интервала, по стаи: активни резервации (type: booking, с идентификатора, за да можете да я прочетете), блокирания и external събития, импортирани от iCal фийдовете, които собственикът е свързал (Airbnb, Booking.com). Пропуснете unit_id, за да получите всички стаи; стая без заетост има "busy": []. Интервали до 366 дни. Всичко, което не е в списъка, е свободно, включително отказаните резервации.
И двете дати са задължителни, а интервалът трябва да е поне един ден:
Отговор · 400
{"error": {"code": "validation_error", "message": "One or more fields are invalid.", "details": {"to": "range must be at most 366 days"}}}
Оферта за цена
Заявка
curl "https://api.cocobook.app/v1/quote?unit_id=b0e562a3-7c1e-4f0a-9d2b-5e8f1a6c3d47&check_in=2026-07-03&check_out=2026-07-06&breakfast_guests=2" \
-H "Authorization: Bearer cb_live_…"
Отговор · 200
{
"unit": {"id": "b0e562a3-7c1e-4f0a-9d2b-5e8f1a6c3d47", "name": "Sea view"},
"check_in": "2026-07-03",
"check_out": "2026-07-06",
"nights": 3,
"currency": "EUR",
"room": 300,
"breakfast": {"guests": 2, "price": 30},
"total": 330,
"per_night": [
{"date": "2026-07-03T00:00:00Z", "rate": 90, "season": "Summer", "weekend": false},
{"date": "2026-07-04T00:00:00Z", "rate": 105, "season": "Summer", "weekend": true},
{"date": "2026-07-05T00:00:00Z", "rate": 105, "season": "Summer", "weekend": true}
]
}
За всяка нощувка: цената на сезона за стаята, ако е зададена (уикенд вариантът ѝ в почивните дни на имота), иначе базовата или уикенд цената на стаята, умножена по коефициента на сезона, иначе само базовата или уикенд цената. season липсва за нощувки извън сезон. Закуската е гости × нощувки × цената на човек за стаята; стая без закуска винаги връща {"guests": 0, "price": 0}. total е room плюс breakfast.price, а резервация, създадена без цена, получава точно тази сума.
Офертата не запазва нищо и не проверява наличността. Обичайният поток за уиджет за резервации е /availability за показване на свободните дати, /quote за показване на цената, след това POST /bookings, като 409 се приема за „някой беше по-бърз“.
Имот и стаи
Имот
Заявка
curl -X PATCH https://api.cocobook.app/v1/property \
-H "Authorization: Bearer cb_live_…" \
-H "Content-Type: application/json" \
-d '{"name": "Sea House", "currency": "EUR", "location": {"country": "BG", "region": "Burgas", "town": "Sozopol"}}'
Отговор · 200
{
"id": "7d2f8a1c-9e4b-4d6a-8c3f-1b5e7a9d2c60",
"name": "Sea House",
"currency": "EUR",
"location": {
"country": "BG",
"country_name": "Bulgaria",
"region": "Burgas",
"town": "Sozopol",
"label": "Sozopol, Bulgaria"
},
"image_url": "https://cocobook.app/static/uploads/properties/7d2f8a1c.jpg",
"created_at": "2026-03-02T15:40:11Z"
}
Променят се само изпратените полета, включително в location, така че {"location": {"town": "Chernomorets"}} запазва държавата и региона. GET /v1/property връща същия обект. Валути: EUR USD GBP TRY BGN RON CHF; country е код по ISO 3166-1 alpha-2; country_name и label са изчислени и само за четене. Промяна в профила веднага обновява сайта за гости на имота и обявата му в „Разгледай имоти“. Снимката не може да се променя през API-то.
Отговор · 400
{"error": {"code": "validation_error", "message": "One or more fields are invalid.", "details": {"currency": "must be one of EUR, USD, GBP, TRY, BGN, RON, CHF", "location.country": "must be an ISO 3166-1 alpha-2 code"}}}
Стаи
Заявка
curl https://api.cocobook.app/v1/units \
-H "Authorization: Bearer cb_live_…"
Отговор · 200
{
"data": [
{
"id": "b0e562a3-7c1e-4f0a-9d2b-5e8f1a6c3d47",
"name": "Sea view",
"type": "apartment",
"capacity": 2,
"floor": "2",
"base_price": 75,
"weekend_price": 90,
"breakfast": true,
"breakfast_price": 5
},
{
"id": "4f7a9c21-3b8d-4e6a-a1c5-2d9e8b7f6a10",
"name": "Garden studio",
"type": "studio",
"capacity": 3,
"floor": "",
"base_price": 80,
"weekend_price": 0,
"breakfast": false,
"breakfast_price": 0
}
]
}
Стаите са само за четене през API-то и списъкът не се разделя на страници. base_price е цената в делничен ден извън сезоните, а weekend_price — цената в почивните дни на имота, като 0 означава същата като base_price. breakfast_price е на човек на нощувка, като 0 означава, че е включена. Сезоните променят реалната цена на нощувката, затова използвайте /quote за точна цена. GET /v1/units/{id} връща самостоятелен обект за една стая; type е едно от studio, apartment, villa, room, loft.
Грешки и лимити
| Код | HTTP | Значение |
|---|---|---|
missing_api_key | 401 | Не е изпратен ключ |
invalid_api_key | 401 | Непознат, отменен или изтекъл ключ |
insufficient_scope | 403 | Ключът няма нужното право |
validation_error | 400 | Грешно поле — вижте details |
invalid_json | 400 | Не е JSON, празно е, над 64 KB или има непознати полета |
not_found | 404 | Няма такъв ресурс в този имот |
unit_unavailable | 409 | Застъпва се друга активна резервация |
unit_blocked | 409 | Застъпва се блокиране |
rate_limited | 429 | Над 100 заявки в минута; изчакайте Retry-After |
internal_error | 500 | Грешка от наша страна; опитайте по-късно |
details присъства при validation_error (по едно съобщение за поле, вложените полета като guest.email или location.country, параметрите на заявката с името им) и при двата 409 (conflicting_booking_ids). Всяка друга грешка съдържа само code и message.
Лимит на заявките
Всеки ключ може да прави 100 заявки в минута. Интервалът е календарната минута, обща за всички наши сървъри, и се нулира в началото на следващата минута, а не 60 секунди след първата ви заявка. Всеки удостоверен отговор носи X-RateLimit-Limit, X-RateLimit-Remaining и X-RateLimit-Reset (секунди до нулирането на интервала); броят се само заявките с валиден ключ. Заявка номер 101 в един интервал получава:
Отговор · 429
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 23
{"error": {"code": "rate_limited", "message": "Too many requests; at most 100 per minute per key."}}
Регулирайте темпото на масов импорт, като следите X-RateLimit-Remaining, а при 429 изчакайте Retry-After секунди, вместо да опитвате веднага.
Добри практики
- Един ключ за всяка интеграция, кръстен на нея, само с правата, които ѝ трябват. Отменяйте ключовете, които вече не ползвате; на краткосрочните ключове задавайте срок на валидност.
- Проверявайте с
updated_since, вместо да изтегляте всичко наново. - Приемайте
409като нормален резултат: прочетете отново/availabilityи предложете други дати. - Повтаряйте
429следRetry-After, а5xx— с нарастващо изчакване, винаги сIdempotency-Keyпри създаване. - Четете само полетата, които ви трябват, и допускайте нови: в рамките на
v1отговорите получават полета, но никога не губят.
Имате въпроси или случай, който API-то още не покрива? Пишете на hello@cocobook.app.