{"openapi":"3.1.0","info":{"title":"Scopeflow API","version":"0.3.1","description":"Early beta. Authenticate with an account-scoped bearer token. Tokens expire after 30 days. Create/revise only after the user reviews the exact content. A scope fee is project metadata; creating or approving a scope does not collect payment. For safe creation retries send the same Idempotency-Key and exact content. Without the header, creation is not idempotent; read scopes before retrying."},"servers":[{"url":"/"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Scope":{"type":"object","additionalProperties":false,"required":["title","client","deliverables","exclusions","fee","currency","revisions","due","notes"],"properties":{"title":{"type":"string","minLength":3,"maxLength":160},"client":{"type":"string","minLength":2,"maxLength":120},"deliverables":{"type":"string","maxLength":5000,"description":"One deliverable per line (at least 5 characters unless items is provided). Rewritten from items when items is provided."},"items":{"type":"array","maxItems":60,"items":{"$ref":"#/components/schemas/Item"}},"client_inputs":{"type":"array","maxItems":20,"items":{"$ref":"#/components/schemas/ClientInput"}},"exclusions":{"type":"string","maxLength":3000},"fee":{"type":"number","minimum":0,"maximum":10000000},"currency":{"type":"string","enum":["USD","INR","GBP","EUR"]},"revisions":{"type":"integer","minimum":0,"maximum":50},"due":{"type":"string","description":"YYYY-MM-DD or empty string"},"notes":{"type":"string","maxLength":3000}}},"Item":{"type":"object","additionalProperties":false,"required":["id","title"],"properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$","description":"Stable across versions; reuse when revising"},"title":{"type":"string","minLength":2,"maxLength":200},"qty":{"type":"integer","minimum":1,"maximum":10000,"default":1},"unit":{"type":"string","maxLength":40,"default":""},"price":{"type":["number","null"],"minimum":0,"default":null},"acceptance":{"type":"string","maxLength":1000,"default":""}}},"ClientInput":{"type":"object","additionalProperties":false,"required":["id","title"],"properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"title":{"type":"string","minLength":2,"maxLength":200},"due":{"type":"string","description":"YYYY-MM-DD or empty string","default":""}}}}},"paths":{"/api/scopes":{"get":{"operationId":"listScopes","summary":"List the latest version of each scope in this account","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"An object containing scopes array"},"400":{"description":"Invalid input"},"401":{"description":"Missing, expired, or revoked API token"},"404":{"description":"Project not found in this account"},"409":{"description":"Concurrent or stale revision; read the project again"},"429":{"description":"Request rate, project, or version limit reached"},"503":{"description":"Storage temporarily unavailable; read back before retrying a creation"}}},"post":{"operationId":"createScope","summary":"Save a fixed scope and create a review link; does not send it","security":[{"bearerAuth":[]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,120}$"},"description":"Reuse for identical creation retries; use a new key for each new scope."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Scope"}}}},"responses":{"200":{"description":"Idempotent replay of an existing scope"},"201":{"description":"Saved scope and review_path. Only the caller receives this link."},"400":{"description":"Invalid input"},"401":{"description":"Missing, expired, or revoked API token"},"404":{"description":"Project not found in this account"},"409":{"description":"Concurrent or stale revision; read the project again"},"429":{"description":"Request rate, project, or version limit reached"},"503":{"description":"Storage temporarily unavailable; read back before retrying a creation"}}}},"/api/scopes/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"project_id from a saved scope"}],"get":{"operationId":"getScopeHistory","summary":"Read all scope versions and their recorded decisions","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"An object containing versions ordered newest first"},"400":{"description":"Invalid input"},"401":{"description":"Missing, expired, or revoked API token"},"404":{"description":"Project not found in this account"},"409":{"description":"Concurrent or stale revision; read the project again"},"429":{"description":"Request rate, project, or version limit reached"},"503":{"description":"Storage temporarily unavailable; read back before retrying a creation"}}},"post":{"operationId":"reviseScope","summary":"Create a fixed new version; previous review links stop accepting decisions","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["expected_version","content"],"properties":{"expected_version":{"type":"integer","minimum":1},"content":{"$ref":"#/components/schemas/Scope"}}}}}},"responses":{"201":{"description":"Saved scope and new review_path"},"400":{"description":"Invalid input"},"401":{"description":"Missing, expired, or revoked API token"},"404":{"description":"Project not found in this account"},"409":{"description":"Concurrent or stale revision; read the project again"},"429":{"description":"Request rate, project, or version limit reached"},"503":{"description":"Storage temporarily unavailable; read back before retrying a creation"}}}},"/api/scopes/{id}/ledger":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"operationId":"getAgreement","summary":"Read the approved baseline, the current agreement (baseline plus approved change orders) and the hash-chained ledger","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"open, reason, baseline, agreement, entries and chain verification"},"400":{"description":"Invalid input"},"401":{"description":"Missing, expired, or revoked API token"},"404":{"description":"Project not found in this account"},"409":{"description":"Concurrent or stale revision; read the project again"},"429":{"description":"Request rate, project, or version limit reached"},"503":{"description":"Storage temporarily unavailable; read back before retrying a creation"}}},"post":{"operationId":"addLedgerEntry","summary":"Append an entry for the approved latest version: change_order, revision_round, client_input, goodwill, or settle (withdrawn only). Change orders apply only after the client approves them on the review link.","security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["kind","baseline_version","payload"],"additionalProperties":false,"properties":{"kind":{"type":"string","enum":["change_order","revision_round","client_input","goodwill","settle"]},"baseline_version":{"type":"integer","minimum":1},"payload":{"type":"object","description":"change_order: {title, reason?, request_quote?, add_items?, remove_item_ids?, fee_delta?, days_delta?, revisions_delta?}; revision_round: {feedback_date, note?}; client_input: {input_id, received, shift?, note?}; goodwill: {title, value, hours?, note?, visible?}; settle: {ref_seq, outcome: withdrawn}"},"request_id":{"type":"string","pattern":"^[A-Za-z0-9_-]{8,120}$","description":"Reuse for identical retries"}}}}}},"responses":{"200":{"description":"Idempotent replay"},"201":{"description":"Recorded; returns the updated agreement"},"400":{"description":"Invalid input"},"401":{"description":"Missing, expired, or revoked API token"},"404":{"description":"Project not found in this account"},"409":{"description":"Concurrent or stale revision; read the project again"},"429":{"description":"Request rate, project, or version limit reached"},"503":{"description":"Storage temporarily unavailable; read back before retrying a creation"}}}},"/api/scopes/{id}/export":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"get":{"operationId":"exportProject","summary":"Export all versions and ledger entries, including private goodwill and review links. Store this private backup securely.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"exported_at, format_version, project_id, versions and ledger_entries"},"400":{"description":"Invalid input"},"401":{"description":"Missing, expired, or revoked API token"},"404":{"description":"Project not found in this account"},"409":{"description":"Concurrent or stale revision; read the project again"},"429":{"description":"Request rate, project, or version limit reached"},"503":{"description":"Storage temporarily unavailable; read back before retrying a creation"}}}},"/api/insights":{"get":{"operationId":"getInsights","summary":"Patterns across approved agreements: change-order value, goodwill, revision rounds used versus sold, client delays, per-client summary","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Insights object"},"400":{"description":"Invalid input"},"401":{"description":"Missing, expired, or revoked API token"},"404":{"description":"Project not found in this account"},"409":{"description":"Concurrent or stale revision; read the project again"},"429":{"description":"Request rate, project, or version limit reached"},"503":{"description":"Storage temporarily unavailable; read back before retrying a creation"}}}}}}