Calendar API¶
The AirDoo Calendar API provides calendar data, availability, and pricing for accommodations.
Authentication¶
All requests require a Bearer Token:
Authorization: Bearer your_api_token
Get the token
In Odoo: AirDoo → Configuration → Settings → Native Odoo API Key
Main Endpoint¶
POST /api/v1/airdoo/calendar¶
Method: POST
Content-Type: application/json
Authentication: Bearer Token required
Request Body¶
{
"accommodation_id": 4,
"month": 2,
"year": 2026
}
| Parameter | Type | Required | Description |
|---|---|---|---|
accommodation_id |
integer | ✅ | Odoo ID of the accommodation |
month |
integer (1-12) | ❌ | Month (default = current month) |
year |
integer | ❌ | Year (default = current year) |
Example Request¶
curl -X POST "https://your-odoo.com/api/v1/airdoo/calendar" \
-H "Authorization: Bearer your_api_token" \
-H "Content-Type: application/json" \
-d '{"accommodation_id": 4, "month": 2, "year": 2026}'
Successful Response (200)¶
{
"success": true,
"data": {
"month": 2,
"year": 2026,
"accommodation_id": 4,
"accommodation_name": "Chalet du Frenalay",
"bookings": [
{
"id": 382,
"confirmation_code": "HM2YEYHZXC",
"title": "Ignace Wentz",
"start_date": "2026-01-27",
"end_date": "2026-02-09",
"nights": 13,
"status": "confirmed",
"source": "airbnb",
"guest_count": 0,
"composition": {
"adults": 1,
"children": 0,
"babies": 0
},
"total_amount": 1810.96,
"color_index": 1,
"notes": ""
}
],
"blocked_dates": ["2026-01-27", "2026-01-28"],
"daily_prices": {
"2026-02-01": 172.5,
"2026-02-02": 172.5
},
"availability_summary": {
"available_nights": 8,
"booked_nights": 20,
"occupancy_rate": 0.714,
"next_available_date": "2026-02-08"
},
"pricing_rules": {
"base_price": 150.0,
"min_stay": 2,
"max_stay": 30,
"weekly_discount": 0.1,
"monthly_discount": 0.2,
"cleaning_fee": 80.0,
"tax_rate": 0.055
}
},
"metadata": {
"api_version": "1.0",
"timestamp": "2026-02-25T23:53:01.073654Z",
"request_id": "9d9ed309"
}
}
Diagnostic Endpoints¶
POST /api/v1/airdoo/calendar/health¶
Calendar service health check.
curl -X POST "https://your-odoo.com/api/v1/airdoo/calendar/health" \
-H "Authorization: Bearer your_api_token" \
-H "Content-Type: application/json" \
-d '{}'
POST /api/v1/airdoo/calendar/test¶
Simple connectivity test.
curl -X POST "https://your-odoo.com/api/v1/airdoo/calendar/test" \
-H "Authorization: Bearer your_api_token" \
-H "Content-Type: application/json" \
-d '{}'
POST /api/v1/airdoo/calendar/debug¶
Returns detailed diagnostic information (module data, available accommodations, etc.).
Error Codes¶
| HTTP Code | Error Code | Description |
|---|---|---|
| 400 | BAD_REQUEST |
Invalid or missing parameters |
| 401 | UNAUTHORIZED |
Missing or invalid Bearer Token |
| 403 | FORBIDDEN |
Access denied |
| 404 | NOT_FOUND |
Accommodation not found |
| 500 | INTERNAL_ERROR |
Server error |
Error Example¶
{
"success": false,
"error": {
"code": 400,
"message": "accommodation_id field required",
"details": {
"parameter": "accommodation_id",
"expected": "integer",
"received": null
}
},
"metadata": {
"api_version": "1.0",
"timestamp": "2026-02-25T23:53:01.956362Z",
"request_id": "187dd7df"
}
}
TypeScript Implementation (Next.js)¶
API Utility¶
// lib/airdoo-api.ts
const API_BASE_URL = process.env.AIRDOO_API_URL; // Odoo backend URL (server-side)
const API_TOKEN = process.env.AIRDOO_API_TOKEN;
export async function getCalendarData(
accommodationId: number,
month?: number,
year?: number
) {
const response = await fetch(`${API_BASE_URL}/api/v1/airdoo/calendar`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
accommodation_id: accommodationId,
...(month && { month }),
...(year && { year }),
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message || 'API request failed');
}
return await response.json();
}
Next.js Security
Never expose the token directly in client code. Call the Odoo API from a Next.js Route Handler (server-side), not from the browser.
Next.js Route Handler (recommended)¶
// app/api/calendar/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const body = await request.json();
const response = await fetch(
`${process.env.AIRDOO_API_URL}/api/v1/airdoo/calendar`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.AIRDOO_API_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
}
);
const data = await response.json();
return NextResponse.json(data);
}
Custom React Hook¶
// hooks/useCalendarData.ts
import { useState, useEffect } from 'react';
interface CalendarData {
bookings: Booking[];
blocked_dates: string[];
daily_prices: Record<string, number>;
availability_summary: AvailabilitySummary;
}
export function useCalendarData(accommodationId: number, month: number, year: number) {
const [data, setData] = useState<CalendarData | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!accommodationId) return;
const fetchData = async () => {
setLoading(true);
setError(null);
try {
// Call your Route Handler (not Odoo directly)
const result = await fetch('/api/calendar', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ accommodation_id: accommodationId, month, year }),
});
const json = await result.json();
setData(json.data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setLoading(false);
}
};
fetchData();
}, [accommodationId, month, year]);
return { data, loading, error };
}
Odoo Configuration¶
Verify the API Token¶
AirDoo → Configuration → Settings- API Settings section → check Native Odoo API Key
Verify Accommodations¶
- Accommodations must exist in
AirDoo → Accommodations - The
accommodation_idcorresponds to the Odoo record ID
Best Practices¶
- Never expose the token client-side — always use a Route Handler
- Cache calendar data (5 minutes recommended)
- Handle errors using standardized codes
- Validate parameters client-side before sending
← Back: API Overview | Next: Booking API →