aauneAdd your business

Aune API

Confirm real service bookings from any agent.

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.

Base URLhttps://aune-homepage-chat.vercel.app
Booking contractpublic_bookings_api_v1
Bookable todayPlumbing/VVS, Electricians, Moving, Cleaning
Truth ruleOnly booking.status=confirmed proves a booking
GET/api/public/requests/capabilities

Read live capabilities

Returns supported verticals, job type IDs, required fields, geographic scope, and non-bookable future verticals.

POST/api/public/booking-options

Search with minimal location data

Checks current provider eligibility, provider-owned fixed price, and concrete slots. Returns short-lived option IDs and exact terms versions.

POST/api/public/bookings

Confirm the chosen option

Revalidates provider, price, slot, consent, terms, and idempotency before creating a durable confirmed booking and calendar entry.

GET/api/public/bookings/{booking_id}

Read confirmed status

Returns redacted provider, slot, price, terms version, and confirmation status without customer contact PII.

POST/api/public/requests

Fallback to provider follow-up

Creates a structured request after consent when no direct option exists. This endpoint does not confirm provider, availability, slot, booking, or final price.

Quickstart

Search before collecting contact details.

Capabilities define the current taxonomy. Booking search then checks real provider price and slot supply without customer contact details.

curl capabilities
curl "$AUNE_BASE_URL/api/public/requests/capabilities"
curl booking search
curl -X POST "$AUNE_BASE_URL/api/public/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 request fallback
curl -X POST "$AUNE_BASE_URL/api/public/requests" \
  -H "Authorization: Bearer $AUNE_PUBLIC_REQUESTS_API_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

One booking contract across REST and MCP.

Both surfaces use postcode-only search followed by an authenticated confirmation carrying the unchanged option, consent evidence, exact terms version, and idempotency key.

TypeScript fetch
const baseUrl = process.env.AUNE_BASE_URL ?? "https://aune-homepage-chat.vercel.app";

const search = await fetch(`${baseUrl}/api/public/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}/api/public/bookings`, {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.AUNE_PUBLIC_REQUESTS_API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    spec_version: "public_bookings_api_v1",
    booking_option_id: option.booking_option_id,
    customer: {
      name: "Anna Andersson",
      phone: "0701234567",
      email: "anna@example.se",
    },
    service_location: {
      country_code: "SE",
      address: "Storgatan 12",
      postcode: "111 20",
    },
    destination_location: {
      country_code: "SE",
      address: "Hornsgatan 20",
      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();
Python requests
import json
import os
import requests

base_url = os.getenv("AUNE_BASE_URL", "https://aune-homepage-chat.vercel.app")
token = os.environ["AUNE_PUBLIC_REQUESTS_API_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}/api/public/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": "Anna Andersson",
        "phone": "0701234567",
        "email": "anna@example.se",
    },
    "service_location": {
        "country_code": "SE",
        "address": "Storgatan 12",
        "postcode": "111 20",
    },
    "destination_location": {
        "country_code": "SE",
        "address": "Hornsgatan 20",
        "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}/api/public/bookings",
    headers={
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json",
    },
    json=payload,
    timeout=20,
)
response.raise_for_status()
print(response.json())
Cleaning request fallback
{
  "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

Confirmation is explicit and inspectable.

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.

201 confirmed booking
{
  "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

Common failure states.

400

Invalid payload or missing required fields.

401

Missing or invalid API token.

422

Unsupported vertical or job_type_id for the selected vertical.

429

Rate limit exceeded.

503

Production token is not configured.