{"openapi":"3.1.0","info":{"title":"IWANA API","version":"1.0.0","description":"The IWANA API lets your systems read and write the same records the dashboard shows:\nrendez-vous, contacts, sites, equipment and the catalogue.\n\n**Authentication.** Every call carries an API key as a bearer token. Create one in\nRéglages → Développeurs. The key embeds its workspace, so there is no workspace\nheader to send and a key can never be pointed at a workspace it does not belong to.\n\n```bash\ncurl -H \"Authorization: Bearer iw_live_...\" \\\\\n  https://www.iwana.site/api/v1/pro/clients?limit=5\n```\n\n**Scopes.** A key carries an explicit scope list, and each scope maps to exactly one\ndashboard permission — a key can never reach something the interface would refuse.\nA call outside the key's scopes is rejected.\n\n**Paging.** Every list takes `limit` and `offset` and returns\n`{ items, total, limit, offset }`. `total` counts the whole match, not the page.\n\n**Compatibility.** Fields are added without notice; treat unknown fields as normal\nand do not fail on them. Removing a field or changing its type means a new major\nversion in the path.","contact":{"name":"IWANA","url":"https://www.iwana.site"}},"servers":[{"url":"https://www.iwana.site/api/v1","description":"Production"}],"tags":[{"name":"Rendez-vous","description":"The appointments themselves — create, reschedule, cancel."},{"name":"Contacts","description":"People and companies you work for."},{"name":"Sites","description":"The addresses work happens at."},{"name":"Équipements","description":"What is installed at a site."},{"name":"Catalogue","description":"Services and locations, configured in the dashboard."},{"name":"Équipe","description":"Members an appointment can be assigned to."}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key minted in Réglages → Développeurs, sent as `Authorization: Bearer iw_live_…`. The secret is shown once at creation and stored only as a hash — if it is lost, revoke it and mint another. Revocation takes effect on the next request."}},"schemas":{"Error":{"type":"object","description":"Every failure has this shape.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"invalid_api_key","description":"Stable, machine-readable."},"message":{"type":"string","description":"Human-readable; may change."},"requestId":{"type":"string","description":"Quote this when asking us about a call."}},"required":["code","message"]}}},"Appointment":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["APPOINTMENT","UNAVAILABILITY"]},"title":{"type":"string","nullable":true},"startAt":{"type":"string","format":"date-time"},"endAt":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["PENDING","AWAITING_CLIENT","CONFIRMED","IN_PROGRESS","COMPLETED","CANCELLED","REQUEST_REJECTED","OFFER_DECLINED","NO_SHOW","RESCHEDULED"]},"clientId":{"type":"string","nullable":true},"serviceId":{"type":"string","nullable":true},"locationId":{"type":"string","nullable":true},"siteId":{"type":"string","nullable":true,"description":"The registered address this happens at."},"internalNotes":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":true}}},"AppointmentInput":{"type":"object","required":["startAt","endAt"],"properties":{"id":{"type":"string","description":"Present = update, absent = create."},"type":{"type":"string","enum":["APPOINTMENT","UNAVAILABILITY"],"default":"APPOINTMENT"},"serviceId":{"type":"string","description":"Required for an APPOINTMENT."},"clientId":{"type":"string","nullable":true},"siteId":{"type":"string","nullable":true},"locationId":{"type":"string","nullable":true},"startAt":{"type":"string","format":"date-time"},"endAt":{"type":"string","format":"date-time"},"status":{"type":"string"},"internalNotes":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":true}}},"Client":{"type":"object","properties":{"id":{"type":"string"},"fullName":{"type":"string"},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"siret":{"type":"string","nullable":true},"address":{"type":"object","additionalProperties":true,"nullable":true},"tags":{"type":"array","items":{"type":"string"}}}},"Site":{"type":"object","properties":{"id":{"type":"string"},"clientId":{"type":"string"},"label":{"type":"string","nullable":true},"address":{"type":"string","description":"The composed one-line address."},"line1":{"type":"string"},"postcode":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"lat":{"type":"number","nullable":true},"lng":{"type":"number","nullable":true},"accessNotes":{"type":"string","nullable":true},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"clientId":{"type":"string","nullable":true},"role":{"type":"string","example":"TENANT"},"name":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"isPrimary":{"type":"boolean"}}}}}},"Equipment":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"brand":{"type":"string","nullable":true},"model":{"type":"string","nullable":true},"serialNumber":{"type":"string","nullable":true},"clientId":{"type":"string"},"siteId":{"type":"string","nullable":true},"nextMaintenanceDueAt":{"type":"string","format":"date-time","nullable":true}}},"Service":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"durationMinutes":{"type":"integer"},"price":{"type":"number","nullable":true},"color":{"type":"string","nullable":true},"active":{"type":"boolean"}}},"Member":{"type":"object","properties":{"id":{"type":"string","description":"Membership id — what an appointment references."},"fullName":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"role":{"type":"string"}}}}},"paths":{"/pro/appointments":{"get":{"tags":["Rendez-vous"],"summary":"List appointments","security":[{"apiKey":["appointments:read"]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":200},"description":"Rows to return."},{"name":"offset","in":"query","schema":{"type":"integer","default":0},"description":"Rows to skip."},{"name":"from","in":"query","schema":{"type":"string","format":"date-time"},"description":"Starts at or after."},{"name":"to","in":"query","schema":{"type":"string","format":"date-time"},"description":"Starts at or before."},{"name":"clientId","in":"query","schema":{"type":"string"}},{"name":"siteId","in":"query","schema":{"type":"string"},"description":"Everything that has happened at one address."},{"name":"statuses","in":"query","schema":{"type":"string"},"description":"Comma-separated."},{"name":"search","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Appointment"}},"total":{"type":"integer","description":"Rows matching the filter, ignoring paging."},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["items","total"]}}}},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Rendez-vous"],"summary":"Create or update an appointment","description":"One endpoint for both: send `id` to update, omit it to create. On an update, an omitted field keeps its stored value and an explicit `null` clears it — which is what lets a reschedule send only the two dates without wiping notes it never read.","security":[{"apiKey":["appointments:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentInput"}}}},"responses":{"201":{"description":"Created or updated.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}}}}}},"400":{"description":"Validation failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pro/appointments/{id}":{"get":{"tags":["Rendez-vous"],"summary":"Get one appointment","security":[{"apiKey":["appointments:read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Appointment"}}}},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Rendez-vous"],"summary":"Cancel an appointment","security":[{"apiKey":["appointments:delete"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Cancelled."},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pro/clients":{"get":{"tags":["Contacts"],"summary":"List contacts","security":[{"apiKey":["clients:read"]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":200},"description":"Rows to return."},{"name":"offset","in":"query","schema":{"type":"integer","default":0},"description":"Rows to skip."},{"name":"search","in":"query","schema":{"type":"string"},"description":"Name, email or phone."},{"name":"view","in":"query","schema":{"type":"string","enum":["list"]},"description":"`list` returns only the columns a table row needs — a much smaller payload."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Client"}},"total":{"type":"integer","description":"Rows matching the filter, ignoring paging."},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["items","total"]}}}},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Contacts"],"summary":"Create or update a contact","security":[{"apiKey":["clients:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Client"}}}},"responses":{"201":{"description":"Created or updated.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}}}}}},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pro/clients/{id}":{"get":{"tags":["Contacts"],"summary":"Get one contact","security":[{"apiKey":["clients:read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Client"}}}},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pro/sites":{"get":{"tags":["Sites"],"summary":"List sites","security":[{"apiKey":["clients:read"]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":200},"description":"Rows to return."},{"name":"offset","in":"query","schema":{"type":"integer","default":0},"description":"Rows to skip."},{"name":"clientId","in":"query","schema":{"type":"string"},"description":"One contact's addresses."},{"name":"search","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Site"}},"total":{"type":"integer","description":"Rows matching the filter, ignoring paging."},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["items","total"]}}}},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Sites"],"summary":"Create a site","security":[{"apiKey":["clients:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}},"responses":{"201":{"description":"Created.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}}}}}},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pro/sites/{id}":{"get":{"tags":["Sites"],"summary":"Get one site, with its people","security":[{"apiKey":["clients:read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pro/equipments":{"get":{"tags":["Équipements"],"summary":"List equipment","security":[{"apiKey":["clients:read"]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":200},"description":"Rows to return."},{"name":"offset","in":"query","schema":{"type":"integer","default":0},"description":"Rows to skip."},{"name":"clientId","in":"query","schema":{"type":"string"}},{"name":"siteId","in":"query","schema":{"type":"string"},"description":"What is installed at one address."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Equipment"}},"total":{"type":"integer","description":"Rows matching the filter, ignoring paging."},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["items","total"]}}}},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pro/catalog/services":{"get":{"tags":["Catalogue"],"summary":"List services","description":"The catalogue is configured in the dashboard; the API reads it.","security":[{"apiKey":["services:read"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Service"}}}}},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pro/team":{"get":{"tags":["Équipe"],"summary":"List team members and teams","security":[{"apiKey":["members:read"]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Member"}}}}},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pro/team/members/invite":{"post":{"tags":["Équipe"],"summary":"Invite a team member (a colleague, NOT a contact)","description":"Adds somebody to the WORKSPACE — a technician, an office manager. This is the endpoint for \"ajouter un membre / un collaborateur / quelqu’un à l’équipe\". For a customer or a prospect, use POST /pro/clients instead.","security":[{"apiKey":["members:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","description":"Where the invitation is sent."},"fullName":{"type":"string","nullable":true},"role":{"type":"string","enum":["OWNER","ADMIN","MEMBER"],"description":"MEMBER by default."},"phone":{"type":"string","nullable":true}}}}}},"responses":{"201":{"description":"Invited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Member"}}}},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pro/team/members/{id}/role":{"patch":{"tags":["Équipe"],"summary":"Change a member’s role","security":[{"apiKey":["members:write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["role"],"properties":{"role":{"type":"string","enum":["OWNER","ADMIN","MEMBER"]}}}}}},"responses":{"200":{"description":"OK"},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pro/team/teams":{"post":{"tags":["Équipe"],"summary":"Create or rename a team (a crew of members)","security":[{"apiKey":["members:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"id":{"type":"string","description":"Omit to create."},"name":{"type":"string"},"color":{"type":"string","nullable":true}}}}}},"responses":{"201":{"description":"Created."},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/pro/team/teams/{id}/members":{"post":{"tags":["Équipe"],"summary":"Add an existing member to a team","security":[{"apiKey":["members:write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["membershipId"],"properties":{"membershipId":{"type":"string"}}}}}},"responses":{"201":{"description":"Added."},"401":{"description":"Missing, malformed, revoked, or out-of-scope credential.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such record in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"x-iwana-scopes":[{"id":"appointments:read","label":"Lire les rendez-vous"},{"id":"appointments:write","label":"Créer et modifier les rendez-vous"},{"id":"appointments:delete","label":"Annuler les rendez-vous"},{"id":"availability:read","label":"Lire les disponibilités"},{"id":"clients:read","label":"Lire les clients"},{"id":"clients:write","label":"Créer et modifier les clients"},{"id":"clients:delete","label":"Supprimer les clients"},{"id":"equipment:read","label":"Lire les équipements"},{"id":"equipment:write","label":"Créer et modifier les équipements"},{"id":"services:read","label":"Lire le catalogue (prestations, lieux)"},{"id":"catalog:write","label":"Créer et modifier les prestations"},{"id":"resources:read","label":"Lire les ressources"},{"id":"resources:write","label":"Créer et modifier les ressources"},{"id":"locations:read","label":"Lire les lieux et adresses"},{"id":"locations:write","label":"Créer et modifier les lieux et adresses"},{"id":"members:read","label":"Lire l'équipe"},{"id":"members:write","label":"Inviter et modifier l'équipe"},{"id":"config:read","label":"Lire la configuration (règles métier)"},{"id":"config:write","label":"Modifier la configuration"},{"id":"projects:read","label":"Lire les chantiers"},{"id":"projects:write","label":"Créer et modifier les chantiers"},{"id":"quotes:read","label":"Lire les devis"},{"id":"quotes:write","label":"Créer et modifier les devis"},{"id":"invoices:read","label":"Lire les factures"},{"id":"invoices:write","label":"Créer et modifier les factures"},{"id":"payments:read","label":"Lire les paiements"},{"id":"payments:write","label":"Enregistrer des paiements"},{"id":"interventions:read","label":"Lire les rapports d'intervention"},{"id":"interventions:write","label":"Créer et modifier les rapports d'intervention"},{"id":"certificates:read","label":"Lire les attestations"},{"id":"certificates:write","label":"Émettre des attestations"},{"id":"notifications:read","label":"Lire les communications"},{"id":"notifications:send","label":"Envoyer des communications"},{"id":"workflows:read","label":"Lire les workflows"},{"id":"workflows:write","label":"Créer et modifier les workflows"},{"id":"workflows:execute","label":"Déclencher des workflows"},{"id":"stats:read","label":"Lire les statistiques"}],"x-iwana-webhook-events":["appointment.created","appointment.updated","appointment.cancelled","client.created","client.updated"]}