Read live capabilities
Returns supported verticals, job type IDs, required fields, geographic scope, and non-bookable future verticals.
Aune API
Search provider, fixed-price, and slot combinations with postcodes but without street address or customer contact data. Confirm the exact option only after user choice, consent, and terms acceptance. Fall back to provider follow-up when no option exists.
Authorization
User-delegated writes use Authorization Code with PKCE S256 and exact OAuth scopes. Public search stays data-minimized. Partner credentials are a separate server-to-server product and must not be exposed in clients.
Returns supported verticals, job type IDs, required fields, geographic scope, and non-bookable future verticals.
Checks current provider eligibility, provider-owned fixed price, and concrete slots. Returns short-lived option IDs and exact terms versions.
Revalidates provider, price, slot, consent, terms, and idempotency before creating a durable confirmed booking and calendar entry.
Returns redacted provider, slot, price, terms version, and lifecycle status without customer contact PII.
Cancels the durable booking idempotently after the user explicitly confirms the destructive action.
Revalidates a newly searched provider option, slot, price, postcodes, consent, and exact terms before moving the booking.
Creates a structured request after consent when no direct option exists. This endpoint does not confirm provider, availability, slot, booking, or final price.
Quickstart
Capabilities define the current taxonomy. Booking search then checks real provider price and slot supply without customer contact details.
curl "$AUNE_BASE_URL/v1/capabilities"curl -X POST "$AUNE_BASE_URL/v1/booking-options" \
-H "Content-Type: application/json" \
--data '{
"spec_version": "public_bookings_api_v1",
"vertical_id": "moving",
"job_type_id": "moving_home",
"description": "Home move tomorrow afternoon from postcode 111 20 to 118 20. The apartment is 45 sqm with about 25 boxes, a sofa, and a bed. Both addresses have elevators.",
"location": {
"country_code": "SE",
"postcode": "111 20",
"city": "Stockholm"
},
"destination_location": {
"country_code": "SE",
"postcode": "118 20",
"city": "Stockholm"
},
"timing": {
"preferred_time_text": "Tomorrow afternoon"
},
"details": {
"home_size_sqm": 45,
"boxes": 25,
"elevator_origin": true,
"elevator_destination": true
},
"locale": "sv-SE",
"timezone": "Europe/Stockholm"
}'curl -X POST "$AUNE_BASE_URL/v1/requests" \
-H "Authorization: Bearer $AUNE_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: partner-case-123" \
--data '{
"spec_version": "public_requests_api_v1",
"vertical_id": "vvs",
"job_type_id": "leak_faucet_fixture",
"description": "Kitchen faucet leaks when used. Customer wants help next week.",
"user_consent_confirmed": true,
"user_consent_statement": "The customer agreed that Aune may receive their contact details and request details for provider follow-up.",
"consent_source": "external_model_conversation",
"customer": {
"name": "Anna Andersson",
"phone": "0701234567",
"email": "anna@example.se"
},
"location": {
"input": "Storgatan 12, 118 61 Stockholm"
},
"timing": {
"preferred_time_text": "Next Friday morning"
},
"budget_sek": 4500,
"details": {
"fixture": "kitchen faucet"
},
"source": "external_model",
"external_reference": "partner-case-123",
"idempotency_key": "partner-case-123",
"locale": "sv-SE",
"timezone": "Europe/Stockholm"
}'Examples
Both surfaces use postcode-only search followed by an authenticated confirmation carrying the unchanged option, consent evidence, exact terms version, and idempotency key.
const baseUrl = process.env.AUNE_BASE_URL ?? "https://aune-homepage-chat.vercel.app";
const search = await fetch(`${baseUrl}/v1/booking-options`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
"spec_version": "public_bookings_api_v1",
"vertical_id": "moving",
"job_type_id": "moving_home",
"description": "Home move tomorrow afternoon from postcode 111 20 to 118 20. The apartment is 45 sqm with about 25 boxes, a sofa, and a bed. Both addresses have elevators.",
"location": {
"country_code": "SE",
"postcode": "111 20",
"city": "Stockholm"
},
"destination_location": {
"country_code": "SE",
"postcode": "118 20",
"city": "Stockholm"
},
"timing": {
"preferred_time_text": "Tomorrow afternoon"
},
"details": {
"home_size_sqm": 45,
"boxes": 25,
"elevator_origin": true,
"elevator_destination": true
},
"locale": "sv-SE",
"timezone": "Europe/Stockholm"
}),
}).then((response) => response.json());
if (!search.direct_booking_available) {
console.log(search.fallback);
process.exit(0);
}
const option = search.options[0]; // Present options and let the user choose.
const response = await fetch(`${baseUrl}/v1/bookings`, {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.AUNE_ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
spec_version: "public_bookings_api_v1",
booking_option_id: option.booking_option_id,
customer: {
name: "Example Customer",
phone: "+46700000000",
email: "customer@example.invalid",
},
service_location: {
country_code: "SE",
address: "Examplegatan 1",
postcode: "111 20",
},
destination_location: {
country_code: "SE",
address: "Exempelvagen 2",
postcode: "118 20",
},
user_consent_confirmed: true,
booking_terms_accepted: true,
booking_terms_version: option.terms.version,
booking_terms_accepted_at: new Date().toISOString(),
idempotency_key: "conversation-123-option-1",
}),
});
const result = await response.json();import json
import os
import requests
base_url = os.getenv("AUNE_BASE_URL", "https://aune-homepage-chat.vercel.app")
token = os.environ["AUNE_ACCESS_TOKEN"]
search_payload = json.loads(r'''{
"spec_version": "public_bookings_api_v1",
"vertical_id": "moving",
"job_type_id": "moving_home",
"description": "Home move tomorrow afternoon from postcode 111 20 to 118 20. The apartment is 45 sqm with about 25 boxes, a sofa, and a bed. Both addresses have elevators.",
"location": {
"country_code": "SE",
"postcode": "111 20",
"city": "Stockholm"
},
"destination_location": {
"country_code": "SE",
"postcode": "118 20",
"city": "Stockholm"
},
"timing": {
"preferred_time_text": "Tomorrow afternoon"
},
"details": {
"home_size_sqm": 45,
"boxes": 25,
"elevator_origin": true,
"elevator_destination": true
},
"locale": "sv-SE",
"timezone": "Europe/Stockholm"
}''')
search = requests.post(
f"{base_url}/v1/booking-options",
json=search_payload,
timeout=20,
).json()
if not search["direct_booking_available"]:
print(search["fallback"])
raise SystemExit(0)
option = search["options"][0] # Present options and let the user choose.
payload = {
"spec_version": "public_bookings_api_v1",
"booking_option_id": option["booking_option_id"],
"customer": {
"name": "Example Customer",
"phone": "+46700000000",
"email": "customer@example.invalid",
},
"service_location": {
"country_code": "SE",
"address": "Examplegatan 1",
"postcode": "111 20",
},
"destination_location": {
"country_code": "SE",
"address": "Exempelvagen 2",
"postcode": "118 20",
},
"user_consent_confirmed": True,
"booking_terms_accepted": True,
"booking_terms_version": option["terms"]["version"],
"booking_terms_accepted_at": "2026-07-10T10:00:00.000Z",
"idempotency_key": "conversation-123-option-1",
}
response = requests.post(
f"{base_url}/v1/bookings",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
},
json=payload,
timeout=20,
)
response.raise_for_status()
print(response.json()){
"spec_version": "public_requests_api_v1",
"vertical_id": "cleaning",
"job_type_id": "cleaning_move_out",
"description": "Move-out cleaning for an empty 82 sqm apartment next Friday.",
"user_consent_confirmed": true,
"user_consent_statement": "The customer agreed that Aune may receive their contact details and request details for provider follow-up.",
"consent_source": "external_model_conversation",
"customer": {
"name": "Anna Andersson",
"phone": "0701234567",
"email": "anna@example.se"
},
"location": {
"input": "Storgatan 12, 118 61 Stockholm"
},
"timing": {
"preferred_time_text": "Next Friday morning"
},
"details": {
"square_meters": 82,
"move_state": "empty"
},
"source": "external_model",
"external_reference": "partner-cleaning-123",
"idempotency_key": "partner-cleaning-123",
"locale": "sv-SE",
"timezone": "Europe/Stockholm"
}Responses
A booking exists only when all three truth flags are true and `booking.status` is `confirmed`. Notification delivery is reported separately and never defines whether the booking exists.
{
"ok": true,
"spec_version": "public_bookings_api_v1",
"request_id": "nvr_booking_...",
"booking": {
"id": "network_...",
"status": "confirmed",
"provider_name": "Example Moving AB",
"service_id": "moving_home_starter_3h",
"slot_start_at": "2026-07-11T13:00:00.000Z",
"slot_end_at": "2026-07-11T16:00:00.000Z",
"price_sek": 2800,
"currency": "SEK",
"terms_version": "fixed_price_terms_v1:moving_home_starter_3h"
},
"booking_created": true,
"availability_checked": true,
"provider_selected": true,
"idempotent_replay": false,
"safe_next_step": "Tell the user the booking is confirmed."
}Errors
Errors use RFC problem details and every response carries request, trace, contract, and server-time metadata.
Fix invalid input; do not retry unchanged.
Refresh authorization or request the missing scope.
Verify opaque ID and authenticated principal.
Preserve idempotency or search a fresh option as instructed.
Obey Retry-After and retry writes only with the same key.