{"openapi":"3.1.0","info":{"title":"Fyndol API","version":"1.0.0","description":"Read your Fyndol agency data and propose changes. Auth: OAuth 2.1 (https://app.fyndol.com/.well-known/oauth-authorization-server) or a sub-account API key (Settings → Webhooks & API → Developers). 120 requests/minute per token. Pagination: limit + offset. Errors: {\"error\",\"code\"}. MCP server: https://app.fyndol.com/mcp. Docs: https://app.fyndol.com/developers"},"servers":[{"url":"https://app.fyndol.com/api/v1"}],"tags":[{"name":"Read"},{"name":"Contacts"},{"name":"Calendars"},{"name":"Opportunities"},{"name":"Changes"}],"paths":{"/sub-accounts":{"get":{"operationId":"getSubAccounts","summary":"List sub-accounts","description":"Sub-accounts (locations) this token can reach, with contact counts.","tags":["Read"],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Items per page"},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0},"description":"Items to skip"}],"security":[{"oauth2":[]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/conversations":{"get":{"operationId":"getConversations","summary":"List conversations","description":"Recent conversations (inbox) with the last message preview, newest first. Scope: conversations.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"unread_only","in":"query","schema":{"type":"boolean"},"description":"Only unread conversations"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Items per page"},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0},"description":"Items to skip"}],"security":[{"oauth2":["conversations.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/opportunities":{"get":{"operationId":"getOpportunities","summary":"List opportunities","description":"Opportunities (deals), most recently updated first. Scope: opportunities.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"pipeline_id","in":"query","schema":{"type":"string"}},{"name":"stage_id","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string"},"description":"open | won | lost | abandoned"},{"name":"q","in":"query","schema":{"type":"string"},"description":"Name contains"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Items per page"},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0},"description":"Items to skip"}],"security":[{"oauth2":["opportunities.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createOpportunity","summary":"Create an opportunity","description":"Same rules as Create a contact, with opportunities.read instead of contacts.read for OAuth tokens; otherwise POST /actions (create_opportunity). With a \"contact\" object the contact is saved first (found by email / phone, or created): when that created or changed a contact, the reply has contact_id with contact_created or contact_updated — also in a 400 when the deal itself is refused (e.g. its stage needs a deal value), so you know the contact was saved.","tags":["Opportunities"],"security":[{"oauth2":["changes.propose"]},{"apiKey":[]}],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"pipeline_id":{"type":"string"},"stage_id":{"type":"string"},"contact_id":{"type":"string"},"contact":{"type":"object"},"name":{"type":"string"},"value":{"type":"number"},"status":{"type":"string"},"assigned_to":{"type":"string"}}}}}},"responses":{"200":{"description":"Updated existing"},"201":{"description":"Created"},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/tags":{"get":{"operationId":"getTags","summary":"List tags","description":"Tags with how many contacts have each. Scope: contacts.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Items per page"},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0},"description":"Items to skip"}],"security":[{"oauth2":["contacts.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/custom-fields":{"get":{"operationId":"getCustomFields","summary":"List custom fields","description":"Contact and opportunity custom fields. Scope: contacts.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Items per page"},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0},"description":"Items to skip"}],"security":[{"oauth2":["contacts.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/workflows":{"get":{"operationId":"getWorkflows","summary":"List workflows","description":"Workflows (automations): status, trigger, step count, contacts still inside. Scope: workflows.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Items per page"},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0},"description":"Items to skip"}],"security":[{"oauth2":["workflows.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/workflows/{id}":{"get":{"operationId":"getWorkflowsById","summary":"Get a workflow","description":"One workflow with its trigger and steps. Scope: workflows.read.","tags":["Read"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"security":[{"oauth2":["workflows.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Data"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/workflow-health":{"get":{"operationId":"getWorkflowHealth","summary":"Workflow health","description":"Problems (errors/warnings) per workflow. Scope: workflows.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Items per page"},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0},"description":"Items to skip"}],"security":[{"oauth2":["workflows.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/calendars":{"get":{"operationId":"getCalendars","summary":"Calendars and upcoming appointments","description":"Calendars plus appointments in a range (default: next 14 days). Scope: calendars.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"from","in":"query","schema":{"type":"string"},"description":"ISO date"},{"name":"to","in":"query","schema":{"type":"string"},"description":"ISO date"}],"security":[{"oauth2":["calendars.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Data"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/sites":{"get":{"operationId":"getSites","summary":"Funnels and websites","description":"Funnels/websites with pages, views and form submissions, plus custom domains. Scope: sites.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"}],"security":[{"oauth2":["sites.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Data"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/reports/summary":{"get":{"operationId":"getReportsSummary","summary":"KPI summary","description":"New leads by source, opportunities created/won/lost, appointments, messages, form submissions over the last N days. Scope: reports.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"days","in":"query","schema":{"type":"integer"},"description":"Look-back window (default 7)"}],"security":[{"oauth2":["reports.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Data"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/reports/client":{"get":{"operationId":"getReportsClient","summary":"Client report","description":"The Client Report numbers (what an agency shows its client). Scope: reports.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"range","in":"query","schema":{"type":"string"},"description":"this_month | last_month | last_7_days | last_30_days | …"},{"name":"from","in":"query","schema":{"type":"string"}},{"name":"to","in":"query","schema":{"type":"string"}}],"security":[{"oauth2":["reports.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Data"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/reports/ai-setter":{"get":{"operationId":"getReportsAiSetter","summary":"AI setter stats","description":"AI call numbers: calls, answered, booked, outcomes, call length, show rate, by lead source. Scope: reports.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"period","in":"query","schema":{"type":"string"},"description":"this_week | last_week | last_4"},{"name":"include_test","in":"query","schema":{"type":"boolean"}}],"security":[{"oauth2":["reports.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Data"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/invoices":{"get":{"operationId":"getInvoices","summary":"List invoices","description":"Invoices (newest first) with status, total, balance, due date and hosted link; plus outstanding / overdue totals. Scope: reports.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"status","in":"query","schema":{"type":"string"},"description":"draft | sent | viewed | partially_paid | paid | overdue | void | refunded"},{"name":"contact_id","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Items per page"},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0},"description":"Items to skip"}],"security":[{"oauth2":["reports.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/payments":{"get":{"operationId":"getPayments","summary":"List payments","description":"Payments (transactions): amount, status, method, contact, invoice number; plus money collected in the last 30 days. Read-only. Scope: reports.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"status","in":"query","schema":{"type":"string"},"description":"succeeded | failed | refunded | partially_refunded"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Items per page"},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0},"description":"Items to skip"}],"security":[{"oauth2":["reports.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/contracts":{"get":{"operationId":"getContracts","summary":"List contracts","description":"Contracts (newest first) with status, signers signed / total, total and dates; plus the contract templates. Read-only: contracts are only sent and signed inside Fyndol. Scope: contacts.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"status","in":"query","schema":{"type":"string"},"description":"draft | sent | viewed | partially_signed | signed | declined | expired | void"},{"name":"contact_id","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Items per page"},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0},"description":"Items to skip"}],"security":[{"oauth2":["contacts.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Page"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/usage":{"get":{"operationId":"getUsage","summary":"AI usage (owner/admin)","description":"AI calls and estimated cost for the agency or one sub-account. OAuth tokens of an owner/admin only. Scope: usage.read.","tags":["Read"],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"from","in":"query","schema":{"type":"string"}},{"name":"to","in":"query","schema":{"type":"string"}}],"security":[{"oauth2":["usage.read"]},{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Data"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/contacts":{"get":{"operationId":"listContacts","summary":"Search contacts","description":"Contacts, newest first. Scope: contacts.read.","tags":["Contacts"],"security":[{"oauth2":["contacts.read"]},{"apiKey":[]}],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"q","in":"query","schema":{"type":"string"},"description":"Name, email or phone contains"},{"name":"email","in":"query","schema":{"type":"string"}},{"name":"tag","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Items per page"},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0},"description":"Items to skip"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"contacts":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}},"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createContact","summary":"Create a contact","description":"API keys with contacts.write. OAuth tokens with changes.propose (or changes.auto) and contacts.read, when the change may run without approval (the connection may make changes without approval, or the sub-account's AI changes setting is “without asking”); otherwise 403 approval_required: use POST /actions (create_contact). Consent to be called or texted (custom ai_call_consent, sms_consent, tcpa_consent, sms_opt_in…) is never turned on here: the contact is still saved without those values, they are listed in ignored_fields and warnings, and a note on the contact says what was sent.","tags":["Contacts"],"security":[{"oauth2":["changes.propose"]},{"apiKey":[]}],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string"},"last_name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"company":{"type":"string"},"source":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"custom":{"type":"object"},"upsert":{"type":"boolean","description":"Update the existing contact with the same email/phone instead of failing"}}}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"contact":{"$ref":"#/components/schemas/Contact"},"created":{"type":"boolean"},"ignored_fields":{"type":"array","items":{"type":"string"},"description":"Consent fields left out, e.g. custom.sms_consent"},"warnings":{"type":"array","items":{"type":"string"}}}}}}},"201":{"description":"Created (same body)"},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/contacts/{id}":{"get":{"operationId":"getContact","summary":"Get a contact","tags":["Contacts"],"security":[{"oauth2":["contacts.read"]},{"apiKey":[]}],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"contact":{"$ref":"#/components/schemas/Contact"}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updateContact","summary":"Update a contact","description":"Same rules as Create a contact, except that turning on consent to be called or texted (custom ai_call_consent, sms_consent, tcpa_consent…) or turning dnd off answers 403 and changes nothing. Sending No, a Yes the contact already gave, or dnd: true works. An empty body changes nothing.","tags":["Contacts"],"security":[{"oauth2":["changes.propose"]},{"apiKey":[]}],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string"},"last_name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"company":{"type":"string"},"custom":{"type":"object"},"dnd":{"type":"boolean"},"assigned_to":{"type":"string"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"contact":{"$ref":"#/components/schemas/Contact"}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/contacts/{id}/tags":{"post":{"operationId":"addContactTags","summary":"Add tags to a contact","description":"Same rules as Create a contact.","tags":["Contacts"],"security":[{"oauth2":["changes.propose"]},{"apiKey":[]}],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tags":{"type":"array","items":{"type":"string"}}},"required":["tags"]}}}},"responses":{"200":{"description":"OK"},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/appointments":{"get":{"operationId":"listAppointments","summary":"List appointments","description":"Default range: last 7 days to next 60 days. Scope: calendars.read.","tags":["Calendars"],"security":[{"oauth2":["calendars.read"]},{"apiKey":[]}],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"},{"name":"from","in":"query","schema":{"type":"string"}},{"name":"to","in":"query","schema":{"type":"string"}},{"name":"calendar_id","in":"query","schema":{"type":"string"}},{"name":"contact_id","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"appointments":{"type":"array","items":{"type":"object"}}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/pipelines":{"get":{"operationId":"listPipelines","summary":"List pipelines with stages","tags":["Opportunities"],"security":[{"oauth2":["opportunities.read"]},{"apiKey":[]}],"parameters":[{"name":"location_id","in":"query","required":false,"schema":{"type":"string"},"description":"Sub-account id (not needed for API keys, tokens limited to one sub-account, or agencies with one sub-account)"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"pipelines":{"type":"array","items":{"type":"object"}}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/actions":{"post":{"operationId":"proposeChange","summary":"Propose a change (OAuth)","tags":["Changes"],"security":[{"oauth2":["changes.propose"]}],"description":"Creates a change. By default it waits for the user's approval in Fyndol (Settings → Connect AI apps → Approvals) and returns 202 with approve_url; if the connection may make changes without approval, or the sub-account's AI changes setting is “without asking”, it runs right away. Tools: stop_active_runs, create_pipeline, add_pipeline_stage, rename_pipeline_stage, create_opportunity, move_opportunity, set_opportunity_status, create_contact, update_contact, tag_contacts, untag_contacts, create_tag, add_contact_note, create_task, create_workflow, update_workflow, create_workflow_draft, create_report_share_link, onboard_client, create_draft_invoice, create_draft_contract. onboard_client, stop_active_runs, create_draft_invoice and create_draft_contract always wait for approval. No tool sends messages, deletes records, bills or reveals keys, but tag, contact and opportunity changes can start the sub-account's published workflows, which may send texts or emails.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tool":{"type":"string","description":"Change name, e.g. tag_contacts"},"input":{"type":"object","description":"e.g. {\"contact_ids\":[\"con_…\"],\"tag\":\"vip\"}"}},"required":["tool"]}}}},"responses":{"200":{"description":"Done (the connection or sub-account allows changes without approval)"},"202":{"description":"Pending approval","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"action_id":{"type":"string"},"summary":{"type":"string"},"approve_url":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/actions/{id}":{"get":{"operationId":"getChange","summary":"Status of a proposed change","tags":["Changes"],"security":[{"oauth2":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"403":{"$ref":"#/components/responses/Error"},"404":{"$ref":"#/components/responses/Error"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"securitySchemes":{"oauth2":{"type":"oauth2","description":"Authorization code + PKCE for apps acting for a person; client credentials for server apps made in Settings → Connect AI apps → Developers.","flows":{"authorizationCode":{"authorizationUrl":"https://app.fyndol.com/oauth/authorize","tokenUrl":"https://app.fyndol.com/oauth/token","refreshUrl":"https://app.fyndol.com/oauth/token","scopes":{"contacts.read":"Read contacts, tags and custom fields","conversations.read":"Read conversations (inbox summaries)","opportunities.read":"Read pipelines and opportunities","workflows.read":"Read workflows and their health","calendars.read":"Read calendars and appointments","sites.read":"Read funnels and websites","reports.read":"Read reports and KPIs","usage.read":"Read AI usage and estimated cost","changes.propose":"Propose changes (you approve them in Fyndol, unless a sub-account is set to “without asking”)","changes.auto":"Allow changes without approval (they can start your published workflows; the app still can’t send, delete, buy or see keys)"}},"clientCredentials":{"tokenUrl":"https://app.fyndol.com/oauth/token","scopes":{"contacts.read":"Read contacts, tags and custom fields","conversations.read":"Read conversations (inbox summaries)","opportunities.read":"Read pipelines and opportunities","workflows.read":"Read workflows and their health","calendars.read":"Read calendars and appointments","sites.read":"Read funnels and websites","reports.read":"Read reports and KPIs","usage.read":"Read AI usage and estimated cost","changes.propose":"Propose changes (you approve them in Fyndol, unless a sub-account is set to “without asking”)"}}}},"apiKey":{"type":"http","scheme":"bearer","description":"Sub-account developer API key (owner/admin create it in Settings → Webhooks & API → Developers)."}},"schemas":{"Page":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object"}},"pagination":{"type":"object","properties":{"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"has_more":{"type":"boolean"},"next_offset":{"type":["integer","null"]}}}}},"Data":{"type":"object","properties":{"data":{}}},"Contact":{"type":"object","properties":{"id":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"company":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"source":{"type":"string"},"created_at":{"type":"string"}}},"Error":{"type":"object","properties":{"error":{"type":"string","description":"Plain-English message"},"code":{"type":"string","description":"Machine-readable code"}},"required":["error"]}},"responses":{"Error":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests (see Retry-After and X-RateLimit-* headers)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}