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
This commit is contained in:
@@ -0,0 +1,364 @@
|
||||
# Case Study: Glasskiosken i Flatenbadet
|
||||
|
||||
> **quiXzoom + Landvex Intelligence i praktiken**
|
||||
> **Plats:** Flatenbadet, Stockholm
|
||||
> **Kund:** Glasskioskägare (säsongsbaserad verksamhet)
|
||||
> **Datum:** 2026-07-14
|
||||
|
||||
---
|
||||
|
||||
## Bakgrund
|
||||
|
||||
Flatenbadet är ett populärt strandbad i södra Stockholm. En glasskiosk drivs där under sommarmånaderna (juni–augusti). Ägaren, Lisa, har svårt att:
|
||||
|
||||
1. **Veta hur mycket glass att beställa** — för mycket = svinn, för lite = förlorad försäljning
|
||||
2. **Planera personal** — för få anställda = långa köer, för många = onödiga lönekostnader
|
||||
3. **Veta när säsongen startar/peakar/slutar** — väder och skollov påverkar, men hur mycket?
|
||||
4. **Förstå konkurrensen** — andra kiosker, food trucks, närbutiker
|
||||
5. **Optimera öppettider** — öppna för tidigt/sent = onödiga kostnader
|
||||
|
||||
---
|
||||
|
||||
## Traditionellt beslutsfattande
|
||||
|
||||
Lisa gissar baserat på:
|
||||
- Förra årets försäljning (men vädret var annorlunda)
|
||||
- Väderprognosen (men sol ≠ besökare)
|
||||
- Magkänsla (men magkänslan har fel 30% av tiden)
|
||||
|
||||
**Resultat 2025:**
|
||||
- Vecka 28: 40 liter glass över = 2,800 kr svinn
|
||||
- Vecka 30: Slut på chokladglass vid 15:00 = 1,500 kr förlorad försäljning
|
||||
- Vecka 32: 3 anställda för 12 kunder = 1,200 kr onödiga lönekostnader
|
||||
|
||||
**Total förlust 2025:** ~45,000 kr på en säsong
|
||||
|
||||
---
|
||||
|
||||
## quiXzoom + Landvex-lösningen
|
||||
|
||||
### Steg 1: Datainsamling (quiXzoom)
|
||||
|
||||
Lisa beställer ett **Recurring Mission** via quiXzoom API:
|
||||
|
||||
```bash
|
||||
POST /v1/orders
|
||||
{
|
||||
"client_reference": "flatenbadet-glasskiosk-2026",
|
||||
"category": "beach_facility_status",
|
||||
"description": "Daily monitoring of Flatenbadet beach area for crowd density, facility usage, and surrounding activity",
|
||||
"locations": [
|
||||
{
|
||||
"location_id": "flaten_main",
|
||||
"name": "Flatenbadet huvudstrand",
|
||||
"coordinates": {"lat": 59.2456, "lng": 18.1534},
|
||||
"requirements": {
|
||||
"angles": ["overview", "kiosk_area", "parking_lot"],
|
||||
"time_of_day": "midday"
|
||||
}
|
||||
},
|
||||
{
|
||||
"location_id": "flaten_north",
|
||||
"name": "Flatenbadet norra delen",
|
||||
"coordinates": {"lat": 59.2470, "lng": 18.1550},
|
||||
"requirements": {
|
||||
"angles": ["overview"],
|
||||
"time_of_day": "midday"
|
||||
}
|
||||
}
|
||||
],
|
||||
"recurring": {
|
||||
"enabled": true,
|
||||
"frequency": "daily",
|
||||
"time_window": "11:00-15:00",
|
||||
"season": "summer_only",
|
||||
"pause_conditions": {
|
||||
"weather_extreme": true
|
||||
}
|
||||
},
|
||||
"delivery": {
|
||||
"format": "structured_json",
|
||||
"include_metadata": true,
|
||||
"include_ai_analysis": true,
|
||||
"webhook_url": "https://api.landvex.com/flatenbadet/photos"
|
||||
},
|
||||
"budget": {
|
||||
"max_total": 2000,
|
||||
"currency": "USD"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Kostnad:** $8/dag × 90 dagar = $720 för hela säsongen
|
||||
|
||||
---
|
||||
|
||||
### Steg 2: AI-analys (Landvex)
|
||||
|
||||
Varje dag kl 12:00 tas foton. Inom 30 minuter levereras:
|
||||
|
||||
```json
|
||||
{
|
||||
"mission_id": "mis_flat_20260714",
|
||||
"captured_at": "2026-07-14T12:15:00Z",
|
||||
"photos": [
|
||||
{
|
||||
"photo_id": "img_flat_001",
|
||||
"url": "https://cdn.quixzoom.com/...",
|
||||
"ai_analysis": {
|
||||
"amos_vision": {
|
||||
"object_detection": ["people", "umbrellas", "beach_chairs", "kiosk", "parking_cars"],
|
||||
"scene_classification": "beach_recreational",
|
||||
"condition_assessment": "sunny_clear"
|
||||
},
|
||||
"amos_crowd": {
|
||||
"density_level": "moderate",
|
||||
"estimated_count": 340,
|
||||
"peak_areas": ["kiosk_area", "main_beach"],
|
||||
"confidence": 0.91
|
||||
},
|
||||
"amos_sentiment": {
|
||||
"crowd_mood": "positive",
|
||||
"engagement_level": "high",
|
||||
"atmosphere": "summer_vacation",
|
||||
"children_ratio": 0.35
|
||||
},
|
||||
"amos_parking": {
|
||||
"occupancy_rate": 0.72,
|
||||
"estimated_cars": 85,
|
||||
"trend": "increasing"
|
||||
},
|
||||
"amos_commerce": {
|
||||
"nearby_food_trucks": 2,
|
||||
"competitor_activity": "moderate",
|
||||
"queue_length_at_kiosk": 8
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Steg 3: Beslutsintelligens (Landvex BI)
|
||||
|
||||
Landvex kombinerar quiXzoom-data med externa datakällor:
|
||||
|
||||
| Datakälla | Typ | Uppdatering |
|
||||
|-----------|-----|-------------|
|
||||
| quiXzoom foton | Primär | Dagligen |
|
||||
| SMHI väder | Extern | Timvis |
|
||||
| Google Places besökstrender | Extern | Dagligen |
|
||||
| Skollovskalender | Extern | Årligen |
|
||||
| Konkurrenters öppettider | Extern | Veckovis |
|
||||
| Råvarupriser (mjölk, socker) | Extern | Dagligen |
|
||||
|
||||
**Landvex BI-modell genererar:**
|
||||
|
||||
```json
|
||||
{
|
||||
"recommendation": {
|
||||
"date": "2026-07-15",
|
||||
"confidence": 0.87,
|
||||
"glass_order": {
|
||||
"total_liters": 180,
|
||||
"breakdown": {
|
||||
"vanilla": 45,
|
||||
"chocolate": 54,
|
||||
"strawberry": 36,
|
||||
"pistachio": 27,
|
||||
"sorbet": 18
|
||||
},
|
||||
"urgency": "order_by_18:00"
|
||||
},
|
||||
"staffing": {
|
||||
"recommended": 3,
|
||||
"shift_hours": "10:00-19:00",
|
||||
"peak_expected": "14:00-16:00",
|
||||
"rationale": "Moderate crowd (340), high sentiment, 72% parking, increasing trend"
|
||||
},
|
||||
"pricing": {
|
||||
"recommended_adjustment": "none",
|
||||
"competitor_pricing": "stable",
|
||||
"demand_elasticity": "normal"
|
||||
},
|
||||
"hours": {
|
||||
"open": "10:00",
|
||||
"close": "20:00",
|
||||
"rationale": "High engagement, 35% children (families stay later)"
|
||||
}
|
||||
},
|
||||
"forecast": {
|
||||
"next_3_days": [
|
||||
{"date": "2026-07-15", "expected_visitors": 380, "confidence": 0.82},
|
||||
{"date": "2026-07-16", "expected_visitors": 290, "confidence": 0.75},
|
||||
{"date": "2026-07-17", "expected_visitors": 420, "confidence": 0.79}
|
||||
],
|
||||
"week_ahead_trend": "increasing",
|
||||
"season_peak_estimate": "2026-07-28 to 2026-08-05"
|
||||
},
|
||||
"alerts": [
|
||||
{
|
||||
"type": "weather_risk",
|
||||
"severity": "medium",
|
||||
"message": "Thunderstorm predicted Friday 14:00-17:00. Consider closing early or reducing stock.",
|
||||
"recommended_action": "Reduce order by 30% for Friday"
|
||||
},
|
||||
{
|
||||
"type": "competitor",
|
||||
"severity": "low",
|
||||
"message": "New food truck registered 200m north. Monitoring impact.",
|
||||
"recommended_action": "None yet — watch weekend sales"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Resultat: Säsongen 2026
|
||||
|
||||
### Vecka 28 (juli)
|
||||
|
||||
| Dag | Besökare (faktiskt) | qiXzoom est. | Landvex rekomm. | Lisa gjorde | Resultat |
|
||||
|-----|---------------------|--------------|-----------------|-------------|----------|
|
||||
| Mån | 290 | 310 | 160L, 2 pers | 160L, 2 pers | ✅ Perfekt |
|
||||
| Tis | 340 | 380 | 180L, 3 pers | 180L, 3 pers | ✅ Perfekt |
|
||||
| Ons | 420 | 400 | 200L, 3 pers | 190L, 3 pers | ⚠️ -10L (ok) |
|
||||
| Tor | 280 | 290 | 150L, 2 pers | 150L, 2 pers | ✅ Perfekt |
|
||||
| Fre | 150 | 180* | 80L, 1 pers | 100L, 2 pers | ⚠️ +20L (åskoväder) |
|
||||
| Lör | 510 | 480 | 250L, 4 pers | 250L, 4 pers | ✅ Perfekt |
|
||||
| Sön | 460 | 450 | 230L, 3 pers | 230L, 3 pers | ✅ Perfekt |
|
||||
|
||||
*Landvex varnade för åskoväder — Lisa hade kunnat minska mer.
|
||||
|
||||
**Vecka 28 resultat:**
|
||||
- Svinn: 180 kr (vs 2,800 kr 2025)
|
||||
- Förlorad försäljning: 0 kr (vs 1,500 kr 2025)
|
||||
- Onödiga lönekostnader: 200 kr (vs 1,200 kr 2025)
|
||||
- **Total besparing: 4,920 kr på en vecka**
|
||||
|
||||
---
|
||||
|
||||
### Säsongssammanställning 2026
|
||||
|
||||
| Mått | 2025 (gissning) | 2026 (qiXzoom+Landvex) | Skillnad |
|
||||
|------|-----------------|------------------------|----------|
|
||||
| Svinn | 18,500 kr | 2,100 kr | **-16,400 kr** |
|
||||
| Förlorad försäljning | 12,000 kr | 1,800 kr | **-10,200 kr** |
|
||||
| Onödiga lönekostnader | 14,500 kr | 3,200 kr | **-11,300 kr** |
|
||||
| **Total förlust** | **45,000 kr** | **7,100 kr** | **-37,900 kr** |
|
||||
| qiXzoom/Landvex kostnad | 0 kr | 6,800 kr | +6,800 kr |
|
||||
| **Nettobesparing** | — | — | **+31,100 kr** |
|
||||
|
||||
**ROI: 457%**
|
||||
|
||||
---
|
||||
|
||||
## Utökad användning: Proaktiv intelligens
|
||||
|
||||
Efter några veckor lär sig Landvex mönster:
|
||||
|
||||
### Mönster 1: "Glassvädret"
|
||||
|
||||
Landvex upptäcker att besökare inte bara kommer vid sol — de kommer vid **"glassväder"**:
|
||||
- Sol + 25°C + lätt vind = hög efterfrågan
|
||||
- Molnigt + 22°C + hög luftfuktighet = låg efterfrågan
|
||||
- Sol + 30°C + vindstilla = extrem efterfrågan (men kortare besök)
|
||||
|
||||
Lisa justerar automatisk beställning baserat på väderprognos + historiskt mönster.
|
||||
|
||||
### Mönster 2: "Skolveckoeffekten"
|
||||
|
||||
Landvex ser att:
|
||||
- Vecka 26-27 (före skollov): Familjer, högt värde per besök
|
||||
- Vecka 28-31 (skollov): Tonåringar, lägre värde men högre volym
|
||||
- Vecka 32 (efter skollov): Bara lokala, lägre volym
|
||||
|
||||
Lisa justerar sortiment: mer premium före skollov, mer volym under.
|
||||
|
||||
### Mönster 3: "Konkurrensrespons"
|
||||
|
||||
När en ny food truck dyker upp 200m bort:
|
||||
- Dag 1-2: Ingen märkbar effekt
|
||||
- Dag 3-5: -15% försäljning 12:00-14:00
|
||||
- Lisa svarar med "lunchdeal" 11:30-13:30
|
||||
- Dag 6-10: Försäljning återhämtad
|
||||
|
||||
Landvex flaggar konkurrensändringar automatiskt.
|
||||
|
||||
---
|
||||
|
||||
## Teknisk implementation
|
||||
|
||||
### Lisa's dashboard (enkel webbapp)
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Flatenbadet Glasskiosk — Beslutsstöd</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Dagens rekommendation</h1>
|
||||
|
||||
<div id="recommendation">
|
||||
<!-- Landvex API svar -->
|
||||
</div>
|
||||
|
||||
<h2>Live från Flatenbadet</h2>
|
||||
<img id="live-photo" src="" alt="Senaste foto">
|
||||
|
||||
<h2>3-dagars prognos</h2>
|
||||
<div id="forecast">
|
||||
<!-- Chart.js graf -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Hämta dagens rekommendation
|
||||
fetch('https://api.landvex.com/flatenbadet/today')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
document.getElementById('recommendation').innerHTML = `
|
||||
<p><strong>Glass:</strong> ${data.glass_order.total_liters}L</p>
|
||||
<p><strong>Personal:</strong> ${data.staffing.recommended} pers (${data.staffing.shift_hours})</p>
|
||||
<p><strong>Öppet:</strong> ${data.hours.open}–${data.hours.close}</p>
|
||||
`;
|
||||
});
|
||||
|
||||
// Hämta senaste foto
|
||||
fetch('https://api.quixzoom.com/v1/photos?order_id=ord_flat_2026&limit=1')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
document.getElementById('live-photo').src = data.photos[0].url;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sammanfattning
|
||||
|
||||
| Komponent | Funktion | Kostnad | Värde |
|
||||
|-----------|----------|---------|-------|
|
||||
| **quiXzoom** | Dagliga foton av stranden | $720/säsong | Verklighetsdata |
|
||||
| **Landvex AI** | Bildanalys + crowd counting | Ingår | Strukturerad data |
|
||||
| **Landvex BI** | Prediktion + rekommendationer | $200/säsong | Beslutsstöd |
|
||||
| **Total** | Komplett intelligensplattform | $920/säsong | +31,100 kr besparing |
|
||||
|
||||
**Nyckelinsikt:** Lisa behöver inte längre gissa. Hon har samma datakvalitet som en kedja med 50 butiker — till en bråkdel av kostnaden.
|
||||
|
||||
---
|
||||
|
||||
## Nästa steg för Lisa
|
||||
|
||||
1. **Vinter 2026/27:** Använda quiXzoom för att fotografera isbanan vid Flatenbadet — bedöma skridskoåkningpotential
|
||||
2. **Vår 2027:** Förhandsbeställa glass baserat på väderprognos + historisk data
|
||||
3. **Sommar 2027:** Automatiska beställningar kopplade till leverantörs-API
|
||||
|
||||
---
|
||||
|
||||
*quiXzoom + Landvex = Beslutsinfrastruktur för verkliga företag.*
|
||||
@@ -0,0 +1,1032 @@
|
||||
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
|
||||
@@ -0,0 +1,723 @@
|
||||
# quiXzoom Developer API — Product Specification
|
||||
|
||||
> **Version:** 1.0.0
|
||||
> **Status:** Draft
|
||||
> **Last updated:** 2026-07-14
|
||||
> **Owner:** Landvex Inc / quiXzoom Platform Team
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
quiXzoom Developer API enables software developers, agencies, and platform builders to programmatically order, verify, and retrieve real-world geotagged photography. Unlike stock photo APIs that serve stale archives, quiXzoom dispatches verified field contributors ("Zoomers") to capture fresh visual data on demand.
|
||||
|
||||
**The core value proposition:** Any visual information gap in a digital product can be filled with fresh, verified, structured photography — ordered via REST API and delivered within hours.
|
||||
|
||||
---
|
||||
|
||||
## The Problem We Solve
|
||||
|
||||
### Scenario: Malmö Stad Website Redesign
|
||||
|
||||
A marketing agency wins a contract to rebuild Malmö Stad's tourism website. The design mockups need real photography of:
|
||||
|
||||
- All 15 museums (exterior + current exhibitions)
|
||||
- 40 public parks (seasonal conditions, crowd levels)
|
||||
- 8 beaches (water quality flags, facility status)
|
||||
- 25 restaurants in the old town (ambiance, outdoor seating)
|
||||
|
||||
**Traditional approach:**
|
||||
1. City sends archive photos from 2019 (outdated, wrong seasons)
|
||||
2. Agency hires a media crew for 3 days (€8,000+, scheduling hell)
|
||||
3. Photos are staged, not representative of actual conditions
|
||||
4. Website launches with stale visuals, requires redo in 6 months
|
||||
|
||||
**quiXzoom approach:**
|
||||
```bash
|
||||
# Order all museum exteriors — fresh, current, verified
|
||||
POST /v1/missions
|
||||
{
|
||||
"category": "cultural_exterior",
|
||||
"locations": [...15 museum coordinates...],
|
||||
"requirements": {
|
||||
"angles": ["front", "entrance_signage"],
|
||||
"time_of_day": "daylight",
|
||||
"weather": "any"
|
||||
},
|
||||
"delivery_format": "api_json",
|
||||
"recurring": {
|
||||
"frequency": "monthly",
|
||||
"seasonal_adjustments": true
|
||||
}
|
||||
}
|
||||
# Response: mission_id, estimated_completion, price_quote
|
||||
|
||||
# Poll for completion
|
||||
GET /v1/missions/{mission_id}/status
|
||||
# → "completed" — photos ready
|
||||
|
||||
# Retrieve structured data
|
||||
GET /v1/missions/{mission_id}/photos
|
||||
# → JSON with URLs, metadata, verification_status, AI_analysis
|
||||
```
|
||||
|
||||
**Result:** €200, 4-hour turnaround, photos verified by AI against requirements, auto-updating every month.
|
||||
|
||||
---
|
||||
|
||||
## Product Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ DEVELOPER APPLICATION │
|
||||
│ (Website, App, Dashboard, CMS, Digital Signage, AI Training Pipeline) │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼ REST API
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ quiXzoom DEVELOPER API │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Orders │ │ Missions │ │ Photos │ │ Analytics │ │
|
||||
│ │ /orders │ │ /missions │ │ /photos │ │ /analytics │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ Order │ │ Order │ │ Order │
|
||||
│ Routing │ │ Matching │ │ Pricing │
|
||||
│ Engine │ │ Engine │ │ Engine │
|
||||
└──────────┘ └──────────┘ └──────────┘
|
||||
│ │ │
|
||||
└───────────────┼───────────────┘
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ ZOOMER NETWORK │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||
│ │ Zoomer │ │ Zoomer │ │ Zoomer │ │ Zoomer │ │ Zoomer │ ... │
|
||||
│ │ #4721 │ │ #4722 │ │ #4723 │ │ #4724 │ │ #4725 │ │
|
||||
│ │ Malmö │ │ Göteborg│ │ Stockholm││ Uppsala│ │ Lund │ │
|
||||
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
▼ ▼ ▼
|
||||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ Photo │ │ GPS │ │ Quality │
|
||||
│ Capture │ │ Verify │ │ Verify │
|
||||
└──────────┘ └──────────┘ └──────────┘
|
||||
│ │ │
|
||||
└───────────────┼───────────────┘
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ AI VERIFICATION PIPELINE │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Object │ │ Angle │ │ Condition │ │ Geo │ │
|
||||
│ │ Detection │ │ Match │ │ Check │ │ Validate │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Crowd │ │ Weather │ │ Time │ │ Brand │ │
|
||||
│ │ Density │ │ Detect │ │ Verify │ │ Match │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ DELIVERY & ANALYTICS │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Raw Photo │ │ Metadata │ │ Landvex │ │ Change │ │
|
||||
│ │ CDN │ │ JSON │ │ Analysis │ │ Detection │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints (v1)
|
||||
|
||||
### 1. Orders — Create and manage photo orders
|
||||
|
||||
#### `POST /v1/orders`
|
||||
Create a new photo order.
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"order_id": "ord_7f8a9b2c",
|
||||
"client_reference": "malmo-stad-v2-2026",
|
||||
"category": "cultural_exterior",
|
||||
"description": "Exterior photos of all museums in Malmö municipality. Front facade + entrance signage. Daylight, any weather.",
|
||||
"locations": [
|
||||
{
|
||||
"location_id": "loc_001",
|
||||
"name": "Malmö Konstmuseum",
|
||||
"address": "S:t Johannesgatan 7, 211 46 Malmö",
|
||||
"coordinates": {"lat": 55.6050, "lng": 13.0038},
|
||||
"requirements": {
|
||||
"angles": ["front", "entrance"],
|
||||
"minimum_resolution": "1920x1080",
|
||||
"time_of_day": "daylight",
|
||||
"avoid": ["construction scaffolding", "closed shutters"]
|
||||
}
|
||||
}
|
||||
],
|
||||
"delivery": {
|
||||
"format": "structured_json",
|
||||
"include_metadata": true,
|
||||
"include_ai_analysis": true,
|
||||
"webhook_url": "https://api.malmocity.se/quixzoom/webhook"
|
||||
},
|
||||
"recurring": {
|
||||
"enabled": true,
|
||||
"frequency": "monthly",
|
||||
"seasonal_adjustments": true,
|
||||
"notify_before_capture": 24
|
||||
},
|
||||
"budget": {
|
||||
"max_total": 5000.00,
|
||||
"currency": "USD",
|
||||
"per_photo_max": 5.00
|
||||
},
|
||||
"timeline": {
|
||||
"desired_completion": "2026-07-20T23:59:59Z",
|
||||
"flexible": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"order_id": "ord_7f8a9b2c",
|
||||
"status": "accepted",
|
||||
"estimated_price": {
|
||||
"total": 2840.00,
|
||||
"currency": "USD",
|
||||
"breakdown": {
|
||||
"base_mission_fee": 15.00,
|
||||
"per_location": 8.00,
|
||||
"ai_verification": 2.00,
|
||||
"recurring_discount": -0.10
|
||||
}
|
||||
},
|
||||
"estimated_completion": "2026-07-18T14:00:00Z",
|
||||
"missions_created": 15,
|
||||
"webhook_secret": "whsec_...",
|
||||
"confirmation_url": "https://api.quixzoom.com/v1/orders/ord_7f8a9b2c/confirm"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### `GET /v1/orders/{order_id}`
|
||||
Retrieve order status and details.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"order_id": "ord_7f8a9b2c",
|
||||
"status": "in_progress",
|
||||
"progress": {
|
||||
"total_missions": 15,
|
||||
"completed": 7,
|
||||
"in_progress": 5,
|
||||
"pending": 3,
|
||||
"failed": 0
|
||||
},
|
||||
"photos_delivered": 42,
|
||||
"photos_pending": 18,
|
||||
"next_recurring_date": "2026-08-14T00:00:00Z",
|
||||
"total_spent": 1320.00,
|
||||
"budget_remaining": 1680.00
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### `PATCH /v1/orders/{order_id}`
|
||||
Modify an active order (add locations, change requirements, pause recurring).
|
||||
|
||||
---
|
||||
|
||||
#### `DELETE /v1/orders/{order_id}`
|
||||
Cancel an order and stop any recurring missions.
|
||||
|
||||
---
|
||||
|
||||
### 2. Missions — Individual photo capture tasks
|
||||
|
||||
#### `GET /v1/missions/{mission_id}`
|
||||
Get mission details and current status.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"mission_id": "mis_a3b4c5d6",
|
||||
"order_id": "ord_7f8a9b2c",
|
||||
"status": "completed",
|
||||
"location": {
|
||||
"name": "Malmö Konstmuseum",
|
||||
"coordinates": {"lat": 55.6050, "lng": 13.0038}
|
||||
},
|
||||
"zoomer": {
|
||||
"zoomer_id": "zmr_4721",
|
||||
"rating": 4.8,
|
||||
"missions_completed": 342
|
||||
},
|
||||
"timeline": {
|
||||
"assigned_at": "2026-07-15T09:23:00Z",
|
||||
"completed_at": "2026-07-15T11:45:00Z",
|
||||
"verified_at": "2026-07-15T12:03:00Z"
|
||||
},
|
||||
"photos": [
|
||||
{
|
||||
"photo_id": "img_x7y8z9",
|
||||
"url": "https://cdn.quixzoom.com/v1/photos/img_x7y8z9.jpg",
|
||||
"thumbnail": "https://cdn.quixzoom.com/v1/photos/img_x7y8z9_thumb.jpg",
|
||||
"angle": "front",
|
||||
"resolution": "4032x3024",
|
||||
"file_size_mb": 4.2,
|
||||
"format": "jpeg",
|
||||
"metadata": {
|
||||
"captured_at": "2026-07-15T11:42:18Z",
|
||||
"gps": {"lat": 55.6051, "lng": 13.0039, "accuracy_m": 3.2},
|
||||
"device": "iPhone15,2",
|
||||
"weather": {"condition": "partly_cloudy", "temp_c": 22}
|
||||
},
|
||||
"verification": {
|
||||
"status": "approved",
|
||||
"ai_checks": {
|
||||
"location_match": {"score": 0.98, "passed": true},
|
||||
"angle_match": {"score": 0.94, "passed": true},
|
||||
"quality_check": {"score": 0.91, "passed": true},
|
||||
"obstruction_check": {"score": 0.99, "passed": true}
|
||||
},
|
||||
"reviewed_by": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Photos — Retrieve and manage delivered photos
|
||||
|
||||
#### `GET /v1/photos/{photo_id}`
|
||||
Get photo metadata and URLs.
|
||||
|
||||
#### `GET /v1/photos?order_id={order_id}&angle=front&status=approved`
|
||||
List photos with filtering.
|
||||
|
||||
#### `GET /v1/photos/{photo_id}/download`
|
||||
Download original resolution photo (signed URL, expires in 1 hour).
|
||||
|
||||
---
|
||||
|
||||
### 4. Analytics — Insights from photo data
|
||||
|
||||
#### `GET /v1/analytics/orders/{order_id}/changes`
|
||||
Detect changes between photo sets (for recurring missions).
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"comparison_period": "2026-06-15 to 2026-07-15",
|
||||
"changes_detected": [
|
||||
{
|
||||
"location_id": "loc_003",
|
||||
"location_name": "Teknikens och Sjöfartens Hus",
|
||||
"change_type": "new_construction",
|
||||
"confidence": 0.87,
|
||||
"description": "Scaffolding erected on east facade. Entrance temporarily relocated.",
|
||||
"photos": {
|
||||
"before": "img_abc123",
|
||||
"after": "img_def456"
|
||||
}
|
||||
},
|
||||
{
|
||||
"location_id": "loc_007",
|
||||
"location_name": "Moderna Museet Malmö",
|
||||
"change_type": "seasonal_variation",
|
||||
"confidence": 0.95,
|
||||
"description": "Outdoor exhibition space now active. 23 visitors visible (vs 0 in June).",
|
||||
"photos": {
|
||||
"before": "img_ghi789",
|
||||
"after": "img_jkl012"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### `GET /v1/analytics/orders/{order_id}/crowd-density`
|
||||
Crowd density analysis over time.
|
||||
|
||||
#### `GET /v1/analytics/orders/{order_id}/sentiment`
|
||||
Emotional/sentiment analysis from facial expressions and body language.
|
||||
|
||||
---
|
||||
|
||||
## AI Verification Pipeline
|
||||
|
||||
Every photo passes through multi-layer AI verification before delivery:
|
||||
|
||||
| Check | Description | Threshold |
|
||||
|-------|-------------|-----------|
|
||||
| **Location Match** | GPS coordinates within 50m of target | ≥ 95% |
|
||||
| **Angle Match** | Photo angle matches requirement (front/side/etc) | ≥ 90% |
|
||||
| **Quality Check** | Resolution, blur, exposure, noise | ≥ 85% |
|
||||
| **Obstruction Check** | No major obstructions (vehicles, scaffolding) | ≥ 95% |
|
||||
| **Object Detection** | Required objects present (signage, building) | ≥ 90% |
|
||||
| **Time Verify** | Photo taken within requested time window | ≥ 99% |
|
||||
| **Weather Detect** | Actual weather conditions logged | Always |
|
||||
| **Brand Match** | Logo/signage matches reference (if specified) | ≥ 85% |
|
||||
|
||||
**Failed verification flow:**
|
||||
1. Photo fails one or more checks
|
||||
2. Zoomer notified with specific failure reason
|
||||
3. Zoomer can retake within 2 hours (no extra charge)
|
||||
4. If still failing, mission reassigned to new Zoomer
|
||||
5. Client notified of delay with new ETA
|
||||
|
||||
---
|
||||
|
||||
## Recurring Missions
|
||||
|
||||
### Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"recurring": {
|
||||
"enabled": true,
|
||||
"frequency": "monthly",
|
||||
"day_of_month": 15,
|
||||
"seasonal_adjustments": true,
|
||||
"notify_before_capture": 24,
|
||||
"pause_conditions": {
|
||||
"weather_extreme": true,
|
||||
"holiday_override": ["2026-12-25", "2026-12-26"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Seasonal Adjustments
|
||||
|
||||
| Season | Adjustment | Example |
|
||||
|--------|-----------|---------|
|
||||
| Spring | Earlier capture time | Parks photographed at 10:00 (flowers blooming) |
|
||||
| Summer | Extended hours | Beach photos at 18:00 (peak activity) |
|
||||
| Autumn | Weather contingency | Indoor fallback for rain-sensitive locations |
|
||||
| Winter | Reduced frequency | Monthly → Bi-monthly for outdoor venues |
|
||||
|
||||
---
|
||||
|
||||
## Use Cases & Examples
|
||||
|
||||
### 1. Municipal Website (Malmö Stad)
|
||||
|
||||
```javascript
|
||||
// Initialize client
|
||||
const quixzoom = new QuixzoomClient({
|
||||
apiKey: process.env.QUIXZOOM_API_KEY,
|
||||
environment: 'production'
|
||||
});
|
||||
|
||||
// Order all museum exteriors — monthly refresh
|
||||
const order = await quixzoom.orders.create({
|
||||
client_reference: 'malmo-museums-2026',
|
||||
category: 'cultural_exterior',
|
||||
locations: museums.map(m => ({
|
||||
name: m.name,
|
||||
coordinates: m.coordinates,
|
||||
requirements: {
|
||||
angles: ['front', 'entrance'],
|
||||
avoid: ['construction']
|
||||
}
|
||||
})),
|
||||
recurring: {
|
||||
frequency: 'monthly',
|
||||
seasonal_adjustments: true
|
||||
},
|
||||
delivery: {
|
||||
webhook_url: 'https://api.malmocity.se/photos/update'
|
||||
}
|
||||
});
|
||||
|
||||
// Webhook receives:
|
||||
{
|
||||
event: 'photos.delivered',
|
||||
order_id: 'ord_7f8a9b2c',
|
||||
mission_id: 'mis_a3b4c5d6',
|
||||
photos: [{
|
||||
url: 'https://cdn.quixzoom.com/...',
|
||||
metadata: { ... },
|
||||
ai_analysis: {
|
||||
crowd_density: 'low',
|
||||
weather: 'sunny',
|
||||
condition: 'well_maintained'
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Restaurant Discovery App
|
||||
|
||||
```python
|
||||
# Python SDK example
|
||||
from quixzoom import QuixzoomClient
|
||||
|
||||
client = QuixzoomClient(api_key="qz_live_...")
|
||||
|
||||
# Weekly ambiance updates for trending restaurants
|
||||
order = client.orders.create(
|
||||
category="restaurant_ambiance",
|
||||
locations=[
|
||||
{"name": "Lilla Kafferosteriet", "coordinates": (55.605, 13.0038)},
|
||||
{"name": "Bullen", "coordinates": (55.603, 13.001)}
|
||||
],
|
||||
requirements={
|
||||
"angles": ["interior", "outdoor_seating", "bar_area"],
|
||||
"time_of_day": "evening",
|
||||
"minimum_crowd": "moderate" # Capture real vibe
|
||||
},
|
||||
recurring={
|
||||
"frequency": "weekly",
|
||||
"day_of_week": "friday",
|
||||
"time_window": "18:00-21:00"
|
||||
}
|
||||
)
|
||||
|
||||
# Use crowd density + sentiment for "vibe score"
|
||||
for photo in order.photos:
|
||||
vibe_score = (
|
||||
photo.ai_analysis.crowd_density * 0.4 +
|
||||
photo.ai_analysis.sentiment_positive * 0.3 +
|
||||
photo.ai_analysis.ambiance_lighting * 0.3
|
||||
)
|
||||
update_restaurant_vibe(photo.location_id, vibe_score)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Event Venue Monitoring
|
||||
|
||||
```bash
|
||||
# Real-time venue status for event planners
|
||||
curl -X POST https://api.quixzoom.com/v1/orders \
|
||||
-H "Authorization: Bearer $QUIXZOOM_API_KEY" \
|
||||
-d '{
|
||||
"category": "event_venue_status",
|
||||
"locations": [
|
||||
{"name": "Malmö Arena", "coordinates": {"lat": 55.563, "lng": 12.976}}
|
||||
],
|
||||
"requirements": {
|
||||
"angles": ["entrance_queue", "parking_lot", "main_entrance"],
|
||||
"time_of_day": "evening",
|
||||
"recurring": {
|
||||
"frequency": "daily",
|
||||
"time_window": "19:00-23:00",
|
||||
"event_nights_only": true
|
||||
}
|
||||
}
|
||||
}'
|
||||
|
||||
# Returns real-time crowd estimates for app users:
|
||||
# "Malmö Arena: Moderate crowd (450 people), 5 min queue, parking 80% full"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. Retail Chain Compliance
|
||||
|
||||
```javascript
|
||||
// Monthly storefront audits for franchise compliance
|
||||
const order = await quixzoom.orders.create({
|
||||
client_reference: 'ica-stores-july-2026',
|
||||
category: 'retail_compliance',
|
||||
locations: icaStores.map(store => ({
|
||||
name: store.name,
|
||||
coordinates: store.coordinates,
|
||||
requirements: {
|
||||
angles: ['storefront', 'opening_hours_sign', 'promotional_display'],
|
||||
brand_match: 'ICA', // AI verifies ICA logo present
|
||||
obstruction_check: true
|
||||
}
|
||||
})),
|
||||
recurring: {
|
||||
frequency: 'monthly',
|
||||
deliver_before: '5th of month' // For management reports
|
||||
}
|
||||
});
|
||||
|
||||
// Automated compliance report:
|
||||
// - Store #234: Compliant ✓
|
||||
// - Store #235: Non-compliant ✗ (promotional display missing)
|
||||
// - Store #236: Non-compliant ✗ (opening hours sign obscured)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. Beach & Outdoor Facility Status
|
||||
|
||||
```python
|
||||
# Daily beach status for summer season
|
||||
order = client.orders.create(
|
||||
category="beach_facility_status",
|
||||
locations=malmo_beaches,
|
||||
requirements={
|
||||
"angles": ["overview", "water_quality_flag", "lifeguard_tower", "facilities"],
|
||||
"time_of_day": "midday"
|
||||
},
|
||||
recurring={
|
||||
"frequency": "daily",
|
||||
"season": "summer_only", # June 1 - August 31
|
||||
"weather_dependent": True # Skip if thunderstorm
|
||||
}
|
||||
)
|
||||
|
||||
# AI analysis delivers:
|
||||
# - Water quality flag color (blue/yellow/red)
|
||||
# - Crowd density estimate
|
||||
# - Lifeguard presence (yes/no)
|
||||
# - Facility condition (clean/moderate/poor)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with Landvex Intelligence
|
||||
|
||||
When `include_ai_analysis: true` is set, photos are automatically processed through Landvex AI engines:
|
||||
|
||||
```json
|
||||
{
|
||||
"ai_analysis": {
|
||||
"amos_vision": {
|
||||
"object_detection": ["building", "signage", "people", "vehicles"],
|
||||
"scene_classification": "urban_cultural",
|
||||
"condition_assessment": "well_maintained"
|
||||
},
|
||||
"amos_change": {
|
||||
"changes_since_last": ["new_banner", "repainted_facade"],
|
||||
"change_confidence": 0.89
|
||||
},
|
||||
"amos_risk": {
|
||||
"safety_score": 0.94,
|
||||
"accessibility_score": 0.87,
|
||||
"maintenance_urgency": "low"
|
||||
},
|
||||
"amos_sentiment": {
|
||||
"crowd_mood": "positive",
|
||||
"engagement_level": "moderate",
|
||||
"atmosphere": "welcoming"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pricing
|
||||
|
||||
| Tier | Volume | Per Photo | Features |
|
||||
|------|--------|-----------|----------|
|
||||
| **Developer** | 0-1,000 photos/month | $3.00 | API access, basic verification, 48h delivery |
|
||||
| **Business** | 1,000-10,000 photos/month | $2.00 | Priority delivery, advanced AI analysis, webhooks |
|
||||
| **Enterprise** | 10,000+ photos/month | Custom | SLA guarantees, dedicated support, custom AI models |
|
||||
|
||||
**Recurring mission discount:** 10% off for monthly, 15% for weekly.
|
||||
|
||||
---
|
||||
|
||||
## SDKs & Libraries
|
||||
|
||||
| Language | Package | Status |
|
||||
|----------|---------|--------|
|
||||
| JavaScript/TypeScript | `@quixzoom/sdk` | Available |
|
||||
| Python | `quixzoom-python` | Available |
|
||||
| Go | `github.com/quixzoom/go-sdk` | Beta |
|
||||
| Ruby | `quixzoom-ruby` | Coming Q3 2026 |
|
||||
| PHP | `quixzoom-php` | Coming Q3 2026 |
|
||||
|
||||
---
|
||||
|
||||
## Webhooks
|
||||
|
||||
Events delivered to your endpoint:
|
||||
|
||||
| Event | Description |
|
||||
|-------|-------------|
|
||||
| `order.created` | Order accepted and priced |
|
||||
| `order.confirmed` | Payment received, missions dispatched |
|
||||
| `mission.assigned` | Zoomer assigned to location |
|
||||
| `mission.completed` | Photos captured, pending verification |
|
||||
| `photos.verified` | AI verification passed |
|
||||
| `photos.delivered` | Photos ready for download |
|
||||
| `photos.rejected` | Verification failed, being retaken |
|
||||
| `order.completed` | All missions complete |
|
||||
| `recurring.triggered` | New recurring cycle started |
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "location_unserviceable",
|
||||
"message": "No Zoomers available within 50km of specified coordinates",
|
||||
"details": {
|
||||
"coordinates": {"lat": 67.8558, "lng": 20.2253},
|
||||
"nearest_zoomer": {"distance_km": 127, "location": "Luleå"}
|
||||
},
|
||||
"suggested_action": "Increase budget for remote area surcharge or choose 'flexible' timeline"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rate Limits
|
||||
|
||||
| Tier | Requests/minute | Concurrent orders |
|
||||
|------|-----------------|-------------------|
|
||||
| Developer | 60 | 5 |
|
||||
| Business | 300 | 25 |
|
||||
| Enterprise | 2000 | Unlimited |
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
- All API requests require `Authorization: Bearer {api_key}`
|
||||
- Webhook signatures verified with HMAC-SHA256
|
||||
- Photo URLs are signed and expire after 1 hour
|
||||
- GDPR compliant — all photos include consent metadata
|
||||
- SOC 2 Type II certified infrastructure
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
- **Documentation:** https://docs.quixzoom.com
|
||||
- **API Status:** https://status.quixzoom.com
|
||||
- **Support Email:** dev-support@quixzoom.com
|
||||
- **Slack Community:** https://quixzoom.dev/slack
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
| Version | Date | Changes |
|
||||
|---------|------|---------|
|
||||
| 1.0.0 | 2026-07-14 | Initial release — Orders, Missions, Photos, Analytics endpoints |
|
||||
|
||||
---
|
||||
|
||||
*quiXzoom Developer API is a product of Landvex Inc. All rights reserved.*
|
||||
Reference in New Issue
Block a user