{
    "openapi": "3.1.1",
    "info": {
        "title": "Sturlason public agent booking over HTTP",
        "version": "1.2.0",
        "description": "No customer setup, API key, account, SDK, browser, cookies or MCP connection. Read public data with GET or call all 10 tools with POST JSON. A verified email code and customer approval are required for actual submission. Private booking tokens belong only in POST bodies. Existing human booking is unchanged.",
        "contact": {
            "email": "fotograf@sturlason.no"
        }
    },
    "servers": [
        {
            "url": "https://www.sturlason.no"
        }
    ],
    "security": [],
    "paths": {
        "/agent-booking/public/tools.php": {
            "get": {
                "operationId": "readPublicBooking",
                "summary": "Discover HTTP usage, list services or find live slots without reservation",
                "description": "No query returns API self-description. tool=list_services returns live public catalog. tool=find_slots requires studio and service; use from/to for dates. Structured passport/family choices can be JSON-encoded public query parameters. No quote tokens, holds, customer information or booking actions through GET.",
                "parameters": [
                    {
                        "in": "query",
                        "name": "tool",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "list_services",
                                "find_slots"
                            ]
                        },
                        "description": "Omit all parameters for self-description; find_slots requires studio and service."
                    },
                    {
                        "in": "query",
                        "name": "studio",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "oslo",
                                "asker"
                            ]
                        },
                        "description": "Public read filter; dates use Europe/Oslo calendar dates."
                    },
                    {
                        "in": "query",
                        "name": "service",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "enum": [
                                "cv_private",
                                "cv_extended",
                                "portrait",
                                "baby",
                                "children",
                                "family",
                                "wedding_standard",
                                "wedding_express",
                                "pass",
                                "student_casting_express",
                                "student_casting_standard"
                            ]
                        },
                        "description": "Public read filter; dates use Europe/Oslo calendar dates."
                    },
                    {
                        "in": "query",
                        "name": "from",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "format": "date"
                        },
                        "description": "Public read filter; dates use Europe/Oslo calendar dates."
                    },
                    {
                        "in": "query",
                        "name": "to",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "format": "date"
                        },
                        "description": "Public read filter; dates use Europe/Oslo calendar dates."
                    },
                    {
                        "in": "query",
                        "name": "familyAdults",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "minimum": 0,
                            "maximum": 8
                        },
                        "description": "Public read filter; dates use Europe/Oslo calendar dates."
                    },
                    {
                        "in": "query",
                        "name": "familyChildAges",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "JSON-encoded array matching the tool selection schema. Never put customer data or booking tokens in the URL."
                    },
                    {
                        "in": "query",
                        "name": "passPeople",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "JSON-encoded array matching the tool selection schema. Never put customer data or booking tokens in the URL."
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Structured tool result. Quote and hold are not submitted bookings. requested means registered request, not separate studio approval.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid JSON or arguments; inspect error and inputSchema.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Unknown or expired private capability.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "405": {
                        "description": "Method or GET tool not allowed. GET must never perform writes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Expired hold, busy operation, changed quote or unsupported change. Inspect status before retrying.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "413": {
                        "description": "Body exceeds 65536 bytes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "415": {
                        "description": "POST requires application/json.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limited; wait and back off. Do not bypass limits.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "503": {
                        "description": "Unavailable or uncertain outcome. Keep the same booking token; retrieve status. reconciliation_required needs studio review.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "operationId": "callBookingTool",
                "summary": "Call any booking tool using one JSON envelope",
                "description": "Use {\"tool\":\"...\",\"arguments\":{...}}. Flow: list_services/find_slots, quote_booking, customer authorization, hold_slot, request_contact_code, submit_booking, get_booking. Never silently substitute services, dates or prices. Only retry a submission with its same private bookingToken.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "oneOf": [
                                    {
                                        "$ref": "#/components/schemas/ListServicesCall"
                                    },
                                    {
                                        "$ref": "#/components/schemas/FindSlotsCall"
                                    },
                                    {
                                        "$ref": "#/components/schemas/QuoteBookingCall"
                                    },
                                    {
                                        "$ref": "#/components/schemas/HoldSlotCall"
                                    },
                                    {
                                        "$ref": "#/components/schemas/RequestContactCodeCall"
                                    },
                                    {
                                        "$ref": "#/components/schemas/SubmitBookingCall"
                                    },
                                    {
                                        "$ref": "#/components/schemas/GetBookingCall"
                                    },
                                    {
                                        "$ref": "#/components/schemas/ReleaseHoldCall"
                                    },
                                    {
                                        "$ref": "#/components/schemas/CancelBookingCall"
                                    },
                                    {
                                        "$ref": "#/components/schemas/MoveBookingCall"
                                    }
                                ]
                            },
                            "examples": {
                                "services": {
                                    "value": {
                                        "tool": "list_services",
                                        "arguments": {}
                                    }
                                },
                                "slots": {
                                    "value": {
                                        "tool": "find_slots",
                                        "arguments": {
                                            "selection": {
                                                "studio": "oslo",
                                                "service": "cv_private"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Structured tool result. Quote and hold are not submitted bookings. requested means registered request, not separate studio approval.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid JSON or arguments; inspect error and inputSchema.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Unknown or expired private capability.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "405": {
                        "description": "Method or GET tool not allowed. GET must never perform writes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Expired hold, busy operation, changed quote or unsupported change. Inspect status before retrying.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "413": {
                        "description": "Body exceeds 65536 bytes.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "415": {
                        "description": "POST requires application/json.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "Rate limited; wait and back off. Do not bypass limits.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    },
                    "503": {
                        "description": "Unavailable or uncertain outcome. Keep the same booking token; retrieve status. reconciliation_required needs studio review.",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "additionalProperties": true
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "ListServicesCall": {
                "type": "object",
                "description": "Les offentlige tjenester og krav før bestilling.",
                "properties": {
                    "tool": {
                        "type": "string",
                        "const": "list_services"
                    },
                    "arguments": {
                        "type": "object",
                        "properties": {},
                        "required": [],
                        "additionalProperties": false
                    }
                },
                "required": [
                    "tool",
                    "arguments"
                ],
                "additionalProperties": false
            },
            "FindSlotsCall": {
                "type": "object",
                "description": "Finn ledige tider. Ingen reservasjon opprettes.",
                "properties": {
                    "tool": {
                        "type": "string",
                        "const": "find_slots"
                    },
                    "arguments": {
                        "type": "object",
                        "properties": {
                            "selection": {
                                "type": "object",
                                "properties": {
                                    "service": {
                                        "type": "string",
                                        "enum": [
                                            "cv_private",
                                            "cv_extended",
                                            "portrait",
                                            "baby",
                                            "children",
                                            "family",
                                            "wedding_standard",
                                            "wedding_express",
                                            "pass",
                                            "student_casting_express",
                                            "student_casting_standard"
                                        ]
                                    },
                                    "studio": {
                                        "type": "string",
                                        "enum": [
                                            "oslo",
                                            "asker"
                                        ]
                                    },
                                    "slotId": {
                                        "type": "string"
                                    },
                                    "passPeople": {
                                        "type": "array",
                                        "minItems": 1,
                                        "maxItems": 4,
                                        "items": {
                                            "type": "object",
                                            "properties": {
                                                "nationality": {
                                                    "type": "string"
                                                },
                                                "documentType": {
                                                    "type": "string",
                                                    "enum": [
                                                        "pass",
                                                        "visa",
                                                        "other"
                                                    ]
                                                },
                                                "package": {
                                                    "type": "string",
                                                    "enum": [
                                                        "print",
                                                        "digital",
                                                        "print_digital"
                                                    ]
                                                },
                                                "extraSets": {
                                                    "type": "array",
                                                    "maxItems": 10,
                                                    "items": {
                                                        "type": "object",
                                                        "properties": {
                                                            "nationality": {
                                                                "type": "string"
                                                            },
                                                            "documentType": {
                                                                "type": "string",
                                                                "enum": [
                                                                    "pass",
                                                                    "visa",
                                                                    "other"
                                                                ]
                                                            }
                                                        },
                                                        "required": [
                                                            "nationality",
                                                            "documentType"
                                                        ],
                                                        "additionalProperties": false
                                                    }
                                                }
                                            },
                                            "required": [
                                                "nationality",
                                                "documentType",
                                                "package",
                                                "extraSets"
                                            ],
                                            "additionalProperties": false
                                        }
                                    },
                                    "familyAdults": {
                                        "type": "integer",
                                        "minimum": 0,
                                        "maximum": 8
                                    },
                                    "familyChildAges": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "maxItems": 8
                                    },
                                    "discountCode": {
                                        "type": "string"
                                    },
                                    "notes": {
                                        "type": "string",
                                        "maxLength": 1600
                                    },
                                    "referenceUrl": {
                                        "type": "string"
                                    }
                                },
                                "required": [
                                    "service",
                                    "studio"
                                ],
                                "additionalProperties": false
                            },
                            "from": {
                                "type": "string"
                            },
                            "to": {
                                "type": "string"
                            }
                        },
                        "required": [
                            "selection"
                        ],
                        "additionalProperties": false
                    }
                },
                "required": [
                    "tool",
                    "arguments"
                ],
                "additionalProperties": false
            },
            "QuoteBookingCall": {
                "type": "object",
                "description": "Beregn bindende valg og pris for en valgt slotId. Returnerer tilbud, ikke bestilling.",
                "properties": {
                    "tool": {
                        "type": "string",
                        "const": "quote_booking"
                    },
                    "arguments": {
                        "type": "object",
                        "properties": {
                            "selection": {
                                "type": "object",
                                "properties": {
                                    "service": {
                                        "type": "string",
                                        "enum": [
                                            "cv_private",
                                            "cv_extended",
                                            "portrait",
                                            "baby",
                                            "children",
                                            "family",
                                            "wedding_standard",
                                            "wedding_express",
                                            "pass",
                                            "student_casting_express",
                                            "student_casting_standard"
                                        ]
                                    },
                                    "studio": {
                                        "type": "string",
                                        "enum": [
                                            "oslo",
                                            "asker"
                                        ]
                                    },
                                    "slotId": {
                                        "type": "string"
                                    },
                                    "passPeople": {
                                        "type": "array",
                                        "minItems": 1,
                                        "maxItems": 4,
                                        "items": {
                                            "type": "object",
                                            "properties": {
                                                "nationality": {
                                                    "type": "string"
                                                },
                                                "documentType": {
                                                    "type": "string",
                                                    "enum": [
                                                        "pass",
                                                        "visa",
                                                        "other"
                                                    ]
                                                },
                                                "package": {
                                                    "type": "string",
                                                    "enum": [
                                                        "print",
                                                        "digital",
                                                        "print_digital"
                                                    ]
                                                },
                                                "extraSets": {
                                                    "type": "array",
                                                    "maxItems": 10,
                                                    "items": {
                                                        "type": "object",
                                                        "properties": {
                                                            "nationality": {
                                                                "type": "string"
                                                            },
                                                            "documentType": {
                                                                "type": "string",
                                                                "enum": [
                                                                    "pass",
                                                                    "visa",
                                                                    "other"
                                                                ]
                                                            }
                                                        },
                                                        "required": [
                                                            "nationality",
                                                            "documentType"
                                                        ],
                                                        "additionalProperties": false
                                                    }
                                                }
                                            },
                                            "required": [
                                                "nationality",
                                                "documentType",
                                                "package",
                                                "extraSets"
                                            ],
                                            "additionalProperties": false
                                        }
                                    },
                                    "familyAdults": {
                                        "type": "integer",
                                        "minimum": 0,
                                        "maximum": 8
                                    },
                                    "familyChildAges": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "maxItems": 8
                                    },
                                    "discountCode": {
                                        "type": "string"
                                    },
                                    "notes": {
                                        "type": "string",
                                        "maxLength": 1600
                                    },
                                    "referenceUrl": {
                                        "type": "string"
                                    }
                                },
                                "required": [
                                    "service",
                                    "studio"
                                ],
                                "additionalProperties": false
                            },
                            "agent": {
                                "type": "string",
                                "enum": [
                                    "claude",
                                    "chatgpt",
                                    "codex",
                                    "other"
                                ]
                            }
                        },
                        "required": [
                            "selection"
                        ],
                        "additionalProperties": false
                    }
                },
                "required": [
                    "tool",
                    "arguments"
                ],
                "additionalProperties": false
            },
            "HoldSlotCall": {
                "type": "object",
                "description": "Hold av en tid midlertidig. Krever bookingToken fra tilbudet.",
                "properties": {
                    "tool": {
                        "type": "string",
                        "const": "hold_slot"
                    },
                    "arguments": {
                        "type": "object",
                        "properties": {
                            "bookingToken": {
                                "type": "string",
                                "pattern": "^[a-f0-9]{64}$",
                                "description": "Privat tilgangsreferanse. Oppbevar sikkert; aldri legg i URL eller offentlige logger."
                            }
                        },
                        "required": [
                            "bookingToken"
                        ],
                        "additionalProperties": false
                    }
                },
                "required": [
                    "tool",
                    "arguments"
                ],
                "additionalProperties": false
            },
            "RequestContactCodeCall": {
                "type": "object",
                "description": "Send engangskode til kundens e-post etter at kunden har oppgitt kontaktdata.",
                "properties": {
                    "tool": {
                        "type": "string",
                        "const": "request_contact_code"
                    },
                    "arguments": {
                        "type": "object",
                        "properties": {
                            "bookingToken": {
                                "type": "string",
                                "pattern": "^[a-f0-9]{64}$",
                                "description": "Privat tilgangsreferanse. Oppbevar sikkert; aldri legg i URL eller offentlige logger."
                            },
                            "contact": {
                                "type": "object",
                                "properties": {
                                    "name": {
                                        "type": "string"
                                    },
                                    "email": {
                                        "type": "string"
                                    },
                                    "phone": {
                                        "type": "string"
                                    }
                                },
                                "required": [
                                    "name",
                                    "email",
                                    "phone"
                                ],
                                "additionalProperties": false
                            }
                        },
                        "required": [
                            "bookingToken",
                            "contact"
                        ],
                        "additionalProperties": false
                    }
                },
                "required": [
                    "tool",
                    "arguments"
                ],
                "additionalProperties": false
            },
            "SubmitBookingCall": {
                "type": "object",
                "description": "Oppretter reell bookingforespørsel og sender meldinger. Kunden må godkjenne tid, pris og opplysninger. Gjenta med samme bookingToken ved nettverksfeil; aldri nytt tilbud.",
                "properties": {
                    "tool": {
                        "type": "string",
                        "const": "submit_booking"
                    },
                    "arguments": {
                        "type": "object",
                        "properties": {
                            "bookingToken": {
                                "type": "string",
                                "pattern": "^[a-f0-9]{64}$",
                                "description": "Privat tilgangsreferanse. Oppbevar sikkert; aldri legg i URL eller offentlige logger."
                            },
                            "code": {
                                "type": "string"
                            },
                            "customerApproved": {
                                "type": "boolean",
                                "description": "True only after the customer has authorized this specific action."
                            },
                            "acceptedPriceMinor": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Exact quoted price in øre (minor NOK units), approved by customer."
                            }
                        },
                        "required": [
                            "bookingToken",
                            "code",
                            "customerApproved",
                            "acceptedPriceMinor"
                        ],
                        "additionalProperties": false
                    }
                },
                "required": [
                    "tool",
                    "arguments"
                ],
                "additionalProperties": false
            },
            "GetBookingCall": {
                "type": "object",
                "description": "Hent status på egen forespørsel. requested betyr mottatt, ikke separat studiobekreftelse.",
                "properties": {
                    "tool": {
                        "type": "string",
                        "const": "get_booking"
                    },
                    "arguments": {
                        "type": "object",
                        "properties": {
                            "bookingToken": {
                                "type": "string",
                                "pattern": "^[a-f0-9]{64}$",
                                "description": "Privat tilgangsreferanse. Oppbevar sikkert; aldri legg i URL eller offentlige logger."
                            }
                        },
                        "required": [
                            "bookingToken"
                        ],
                        "additionalProperties": false
                    }
                },
                "required": [
                    "tool",
                    "arguments"
                ],
                "additionalProperties": false
            },
            "ReleaseHoldCall": {
                "type": "object",
                "description": "Frigi en midlertidig reservasjon. Avbestiller ikke en sendt forespørsel.",
                "properties": {
                    "tool": {
                        "type": "string",
                        "const": "release_hold"
                    },
                    "arguments": {
                        "type": "object",
                        "properties": {
                            "bookingToken": {
                                "type": "string",
                                "pattern": "^[a-f0-9]{64}$",
                                "description": "Privat tilgangsreferanse. Oppbevar sikkert; aldri legg i URL eller offentlige logger."
                            }
                        },
                        "required": [
                            "bookingToken"
                        ],
                        "additionalProperties": false
                    }
                },
                "required": [
                    "tool",
                    "arguments"
                ],
                "additionalProperties": false
            },
            "CancelBookingCall": {
                "type": "object",
                "description": "Avbestill en reell agentbooking etter kundens uttrykkelige instruks.",
                "properties": {
                    "tool": {
                        "type": "string",
                        "const": "cancel_booking"
                    },
                    "arguments": {
                        "type": "object",
                        "properties": {
                            "bookingToken": {
                                "type": "string",
                                "pattern": "^[a-f0-9]{64}$",
                                "description": "Privat tilgangsreferanse. Oppbevar sikkert; aldri legg i URL eller offentlige logger."
                            },
                            "customerApproved": {
                                "type": "boolean",
                                "description": "True only after the customer has authorized this specific action."
                            }
                        },
                        "required": [
                            "bookingToken",
                            "customerApproved"
                        ],
                        "additionalProperties": false
                    }
                },
                "required": [
                    "tool",
                    "arguments"
                ],
                "additionalProperties": false
            },
            "MoveBookingCall": {
                "type": "object",
                "description": "Flytt egen agentbooking til et nytt reservert tilbud med samme tjeneste, studio, valg og pris, etter kundens godkjenning.",
                "properties": {
                    "tool": {
                        "type": "string",
                        "const": "move_booking"
                    },
                    "arguments": {
                        "type": "object",
                        "properties": {
                            "bookingToken": {
                                "type": "string",
                                "pattern": "^[a-f0-9]{64}$",
                                "description": "Privat tilgangsreferanse. Oppbevar sikkert; aldri legg i URL eller offentlige logger."
                            },
                            "newQuoteToken": {
                                "type": "string",
                                "pattern": "^[a-f0-9]{64}$",
                                "description": "Privat tilgangsreferanse. Oppbevar sikkert; aldri legg i URL eller offentlige logger."
                            },
                            "customerApproved": {
                                "type": "boolean",
                                "description": "True only after the customer has authorized this specific action."
                            },
                            "acceptedPriceMinor": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Exact quoted price in øre (minor NOK units), approved by customer."
                            }
                        },
                        "required": [
                            "bookingToken",
                            "newQuoteToken",
                            "customerApproved",
                            "acceptedPriceMinor"
                        ],
                        "additionalProperties": false
                    }
                },
                "required": [
                    "tool",
                    "arguments"
                ],
                "additionalProperties": false
            }
        }
    },
    "externalDocs": {
        "description": "HTTP quick start and exact request examples",
        "url": "https://www.sturlason.no/agent-booking/public/http-guide.txt"
    }
}
