Files
boc/docs/products/quixzoom-dev-api-openapi.yaml
T
Bernt 1a12fb870b dev-api: add developer portal, OpenAPI spec, and Flatenbadet case study
- New /developers/ page with API docs, SDKs, pricing, use cases
- OpenAPI 3.0 spec for Orders, Missions, Photos, Analytics
- Case study: Glasskiosken i Flatenbadet — complete ROI analysis
- Updated /order/ with recurring missions and frequency dropdown
2026-07-14 15:27:33 +00:00

1033 lines
28 KiB
YAML

openapi: 3.0.3
info:
title: quiXzoom Developer API
description: |
Programmatically order, verify, and retrieve real-world geotagged photography.
**Key capabilities:**
- Order fresh photos of any physical location
- AI-verified against your requirements
- Recurring missions for ongoing monitoring
- Structured metadata + optional Landvex AI analysis
**Base URL:** `https://api.quixzoom.com/v1`
**Authentication:** Bearer token in Authorization header
version: 1.0.0
contact:
name: quiXzoom Developer Support
email: dev-support@quixzoom.com
url: https://docs.quixzoom.com
license:
name: Landvex Inc Terms of Service
url: https://www.quixzoom.com/terms/
servers:
- url: https://api.quixzoom.com/v1
description: Production
- url: https://api.sandbox.quixzoom.com/v1
description: Sandbox (test data, no real missions)
security:
- BearerAuth: []
paths:
/orders:
post:
summary: Create a new photo order
description: |
Create an order for fresh photography at specified locations.
The order is priced immediately and missions are dispatched once confirmed.
**Example use cases:**
- Municipal websites needing current photos of public facilities
- Restaurant apps wanting real ambiance shots
- Retail chains verifying storefront compliance
- Event venues monitoring crowd levels
operationId: createOrder
tags:
- Orders
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/OrderRequest'
examples:
museum-exteriors:
summary: Museum exteriors — monthly recurring
value:
client_reference: "malmo-museums-2026"
category: "cultural_exterior"
description: "Exterior photos of all museums in Malmö municipality"
locations:
- location_id: "loc_001"
name: "Malmö Konstmuseum"
coordinates:
lat: 55.6050
lng: 13.0038
requirements:
angles: ["front", "entrance"]
time_of_day: "daylight"
delivery:
format: "structured_json"
include_metadata: true
include_ai_analysis: true
webhook_url: "https://api.malmocity.se/quixzoom/webhook"
recurring:
enabled: true
frequency: "monthly"
budget:
max_total: 5000.00
currency: "USD"
beach-status:
summary: Daily beach status monitoring
value:
client_reference: "malmo-beaches-summer"
category: "beach_facility_status"
locations:
- location_id: "beach_001"
name: "Ribersborgsstranden"
coordinates:
lat: 55.6050
lng: 12.9780
requirements:
angles: ["overview", "water_quality_flag", "facilities"]
time_of_day: "midday"
recurring:
enabled: true
frequency: "daily"
season: "summer_only"
responses:
'201':
description: Order created successfully
content:
application/json:
schema:
$ref: '#/components/schemas/OrderResponse'
'400':
description: Invalid request
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'402':
description: Payment required — budget insufficient
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'422':
description: Location unserviceable
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
get:
summary: List orders
description: Retrieve all orders for your account with optional filtering
operationId: listOrders
tags:
- Orders
parameters:
- name: status
in: query
schema:
type: string
enum: [pending, confirmed, in_progress, completed, cancelled]
- name: category
in: query
schema:
type: string
- name: recurring
in: query
schema:
type: boolean
- name: limit
in: query
schema:
type: integer
default: 20
maximum: 100
- name: offset
in: query
schema:
type: integer
default: 0
responses:
'200':
description: List of orders
content:
application/json:
schema:
type: object
properties:
orders:
type: array
items:
$ref: '#/components/schemas/OrderSummary'
total:
type: integer
limit:
type: integer
offset:
type: integer
/orders/{order_id}:
get:
summary: Get order details
description: Retrieve full order status, progress, and delivery information
operationId: getOrder
tags:
- Orders
parameters:
- name: order_id
in: path
required: true
schema:
type: string
pattern: '^ord_[a-zA-Z0-9]+$'
responses:
'200':
description: Order details
content:
application/json:
schema:
$ref: '#/components/schemas/OrderDetail'
'404':
description: Order not found
patch:
summary: Update order
description: |
Modify an active order. Supported changes:
- Add/remove locations
- Update requirements
- Pause/resume recurring
- Update webhook URL
operationId: updateOrder
tags:
- Orders
parameters:
- name: order_id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/OrderUpdate'
responses:
'200':
description: Order updated
'400':
description: Invalid update
'409':
description: Cannot modify — order already completed
delete:
summary: Cancel order
description: Cancel order and stop any recurring missions
operationId: cancelOrder
tags:
- Orders
parameters:
- name: order_id
in: path
required: true
schema:
type: string
responses:
'200':
description: Order cancelled
'404':
description: Order not found
/orders/{order_id}/confirm:
post:
summary: Confirm and pay for order
description: |
Confirm the order quote and initiate payment.
Missions are dispatched immediately after payment confirmation.
operationId: confirmOrder
tags:
- Orders
parameters:
- name: order_id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
payment_method:
type: string
enum: [card, invoice, qz_tokens]
description: Payment method
responses:
'200':
description: Order confirmed, missions dispatched
'402':
description: Payment failed
/missions/{mission_id}:
get:
summary: Get mission details
description: Retrieve mission status, photos, and verification results
operationId: getMission
tags:
- Missions
parameters:
- name: mission_id
in: path
required: true
schema:
type: string
pattern: '^mis_[a-zA-Z0-9]+$'
responses:
'200':
description: Mission details
content:
application/json:
schema:
$ref: '#/components/schemas/MissionDetail'
/photos:
get:
summary: List photos
description: Retrieve photos with filtering by order, mission, status, angle
operationId: listPhotos
tags:
- Photos
parameters:
- name: order_id
in: query
schema:
type: string
- name: mission_id
in: query
schema:
type: string
- name: status
in: query
schema:
type: string
enum: [pending, verified, rejected, approved]
- name: angle
in: query
schema:
type: string
- name: limit
in: query
schema:
type: integer
default: 20
maximum: 100
- name: offset
in: query
schema:
type: integer
default: 0
responses:
'200':
description: List of photos
content:
application/json:
schema:
type: object
properties:
photos:
type: array
items:
$ref: '#/components/schemas/Photo'
total:
type: integer
/photos/{photo_id}:
get:
summary: Get photo metadata
description: Retrieve photo metadata, URLs, and AI analysis
operationId: getPhoto
tags:
- Photos
parameters:
- name: photo_id
in: path
required: true
schema:
type: string
pattern: '^img_[a-zA-Z0-9]+$'
responses:
'200':
description: Photo metadata
content:
application/json:
schema:
$ref: '#/components/schemas/Photo'
/photos/{photo_id}/download:
get:
summary: Download original photo
description: |
Get a signed URL to download the original resolution photo.
URL expires after 1 hour.
operationId: downloadPhoto
tags:
- Photos
parameters:
- name: photo_id
in: path
required: true
schema:
type: string
responses:
'200':
description: Signed download URL
content:
application/json:
schema:
type: object
properties:
download_url:
type: string
format: uri
expires_at:
type: string
format: date-time
/analytics/orders/{order_id}/changes:
get:
summary: Detect changes between photo sets
description: |
Compare photos from different time periods to detect changes.
Only available for recurring missions with 2+ completed cycles.
operationId: getChanges
tags:
- Analytics
parameters:
- name: order_id
in: path
required: true
schema:
type: string
- name: period_start
in: query
schema:
type: string
format: date
- name: period_end
in: query
schema:
type: string
format: date
responses:
'200':
description: Change detection results
content:
application/json:
schema:
$ref: '#/components/schemas/ChangeDetectionResult'
/analytics/orders/{order_id}/crowd-density:
get:
summary: Crowd density analysis
description: Analyze crowd density trends over time
operationId: getCrowdDensity
tags:
- Analytics
parameters:
- name: order_id
in: path
required: true
schema:
type: string
responses:
'200':
description: Crowd density data
content:
application/json:
schema:
type: object
properties:
location_id:
type: string
location_name:
type: string
measurements:
type: array
items:
type: object
properties:
date:
type: string
format: date
density_level:
type: string
enum: [empty, low, moderate, high, very_high]
estimated_count:
type: integer
confidence:
type: number
minimum: 0
maximum: 1
/analytics/orders/{order_id}/sentiment:
get:
summary: Sentiment analysis
description: Analyze emotional tone and engagement from photos
operationId: getSentiment
tags:
- Analytics
parameters:
- name: order_id
in: path
required: true
schema:
type: string
responses:
'200':
description: Sentiment analysis results
content:
application/json:
schema:
type: object
properties:
overall_mood:
type: string
enum: [very_negative, negative, neutral, positive, very_positive]
engagement_level:
type: string
enum: [low, moderate, high]
atmosphere:
type: string
trends:
type: array
items:
type: object
properties:
date:
type: string
format: date
mood_score:
type: number
minimum: 0
maximum: 1
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: API key
description: |
Your quiXzoom API key. Get one at https://www.quixzoom.com/developers
Example: `Authorization: Bearer qz_live_abc123...`
schemas:
OrderRequest:
type: object
required:
- category
- locations
properties:
client_reference:
type: string
description: Your internal reference ID
example: "malmo-museums-2026"
category:
type: string
description: Photo category
enum:
- cultural_exterior
- cultural_interior
- restaurant_ambiance
- retail_compliance
- event_venue_status
- beach_facility_status
- park_recreation
- infrastructure_condition
- custom
description:
type: string
description: Detailed description of what you need
example: "Exterior photos of all museums in Malmö municipality"
locations:
type: array
minItems: 1
maxItems: 1000
items:
$ref: '#/components/schemas/Location'
requirements:
$ref: '#/components/schemas/PhotoRequirements'
delivery:
$ref: '#/components/schemas/DeliveryConfig'
recurring:
$ref: '#/components/schemas/RecurringConfig'
budget:
$ref: '#/components/schemas/BudgetConfig'
timeline:
$ref: '#/components/schemas/TimelineConfig'
Location:
type: object
required:
- coordinates
properties:
location_id:
type: string
description: Your internal location ID
name:
type: string
description: Human-readable location name
example: "Malmö Konstmuseum"
address:
type: string
example: "S:t Johannesgatan 7, 211 46 Malmö"
coordinates:
type: object
required:
- lat
- lng
properties:
lat:
type: number
minimum: -90
maximum: 90
example: 55.6050
lng:
type: number
minimum: -180
maximum: 180
example: 13.0038
requirements:
$ref: '#/components/schemas/PhotoRequirements'
PhotoRequirements:
type: object
properties:
angles:
type: array
items:
type: string
enum: [front, back, side, entrance, interior, overview, detail, custom]
example: ["front", "entrance"]
minimum_resolution:
type: string
pattern: '^\d+x\d+$'
example: "1920x1080"
time_of_day:
type: string
enum: [dawn, morning, midday, afternoon, evening, night, any]
weather:
type: string
enum: [sunny, cloudy, any]
avoid:
type: array
items:
type: string
example: ["construction scaffolding", "closed shutters"]
brand_match:
type: string
description: Verify specific brand/logo is visible
example: "ICA"
minimum_crowd:
type: string
enum: [empty, low, moderate, high, any]
description: Minimum crowd level for ambiance shots
DeliveryConfig:
type: object
properties:
format:
type: string
enum: [structured_json, raw_urls, zip_archive]
default: structured_json
include_metadata:
type: boolean
default: true
include_ai_analysis:
type: boolean
default: false
webhook_url:
type: string
format: uri
description: URL to receive webhook events
RecurringConfig:
type: object
properties:
enabled:
type: boolean
default: false
frequency:
type: string
enum: [daily, weekly, bi_weekly, monthly, quarterly]
day_of_week:
type: integer
minimum: 1
maximum: 7
description: For weekly frequency (1=Monday)
day_of_month:
type: integer
minimum: 1
maximum: 31
time_window:
type: string
pattern: '^\d{2}:\d{2}-\d{2}:\d{2}$'
example: "09:00-17:00"
seasonal_adjustments:
type: boolean
default: false
season:
type: string
enum: [year_round, summer_only, winter_only]
notify_before_capture:
type: integer
description: Hours before capture to notify
example: 24
pause_conditions:
type: object
properties:
weather_extreme:
type: boolean
holiday_override:
type: array
items:
type: string
format: date
BudgetConfig:
type: object
properties:
max_total:
type: number
minimum: 0
currency:
type: string
default: USD
per_photo_max:
type: number
minimum: 0
TimelineConfig:
type: object
properties:
desired_completion:
type: string
format: date-time
flexible:
type: boolean
default: false
OrderResponse:
type: object
properties:
order_id:
type: string
example: "ord_7f8a9b2c"
status:
type: string
enum: [accepted, pending_confirmation]
estimated_price:
$ref: '#/components/schemas/PriceEstimate'
estimated_completion:
type: string
format: date-time
missions_created:
type: integer
webhook_secret:
type: string
confirmation_url:
type: string
format: uri
PriceEstimate:
type: object
properties:
total:
type: number
example: 2840.00
currency:
type: string
example: "USD"
breakdown:
type: object
properties:
base_mission_fee:
type: number
per_location:
type: number
ai_verification:
type: number
recurring_discount:
type: number
OrderSummary:
type: object
properties:
order_id:
type: string
client_reference:
type: string
status:
type: string
category:
type: string
created_at:
type: string
format: date-time
total_locations:
type: integer
photos_delivered:
type: integer
recurring:
type: boolean
OrderDetail:
allOf:
- $ref: '#/components/schemas/OrderSummary'
- type: object
properties:
progress:
type: object
properties:
total_missions:
type: integer
completed:
type: integer
in_progress:
type: integer
pending:
type: integer
failed:
type: integer
photos_delivered:
type: integer
photos_pending:
type: integer
next_recurring_date:
type: string
format: date-time
total_spent:
type: number
budget_remaining:
type: number
locations:
type: array
items:
$ref: '#/components/schemas/Location'
OrderUpdate:
type: object
properties:
locations:
type: array
items:
$ref: '#/components/schemas/Location'
requirements:
$ref: '#/components/schemas/PhotoRequirements'
recurring:
$ref: '#/components/schemas/RecurringConfig'
webhook_url:
type: string
format: uri
status:
type: string
enum: [paused, active]
MissionDetail:
type: object
properties:
mission_id:
type: string
order_id:
type: string
status:
type: string
enum: [pending, assigned, in_progress, completed, failed, cancelled]
location:
$ref: '#/components/schemas/Location'
zoomer:
type: object
properties:
zoomer_id:
type: string
rating:
type: number
missions_completed:
type: integer
timeline:
type: object
properties:
assigned_at:
type: string
format: date-time
completed_at:
type: string
format: date-time
verified_at:
type: string
format: date-time
photos:
type: array
items:
$ref: '#/components/schemas/Photo'
Photo:
type: object
properties:
photo_id:
type: string
url:
type: string
format: uri
thumbnail:
type: string
format: uri
angle:
type: string
resolution:
type: string
file_size_mb:
type: number
format:
type: string
metadata:
type: object
properties:
captured_at:
type: string
format: date-time
gps:
type: object
properties:
lat:
type: number
lng:
type: number
accuracy_m:
type: number
device:
type: string
weather:
type: object
properties:
condition:
type: string
temp_c:
type: number
verification:
type: object
properties:
status:
type: string
enum: [pending, approved, rejected]
ai_checks:
type: object
additionalProperties:
type: object
properties:
score:
type: number
passed:
type: boolean
reviewed_by:
type: string
nullable: true
ai_analysis:
type: object
properties:
amos_vision:
type: object
properties:
object_detection:
type: array
items:
type: string
scene_classification:
type: string
condition_assessment:
type: string
amos_change:
type: object
properties:
changes_since_last:
type: array
items:
type: string
change_confidence:
type: number
amos_risk:
type: object
properties:
safety_score:
type: number
accessibility_score:
type: number
maintenance_urgency:
type: string
amos_sentiment:
type: object
properties:
crowd_mood:
type: string
engagement_level:
type: string
atmosphere:
type: string
ChangeDetectionResult:
type: object
properties:
comparison_period:
type: string
changes_detected:
type: array
items:
type: object
properties:
location_id:
type: string
location_name:
type: string
change_type:
type: string
enum: [new_construction, renovation, seasonal_variation, damage, new_signage, removed_object, crowd_change, other]
confidence:
type: number
description:
type: string
photos:
type: object
properties:
before:
type: string
after:
type: string
Error:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
details:
type: object
suggested_action:
type: string