openapi

{"components":{"responses":{},"schemas":{"WebhookEndpointsResponse":{"description":"Response containing a list of webhook endpoints","properties":{"data":{"description":"List of webhook endpoints","items":{"$ref":"#/components/schemas/WebhookEndpoint"},"type":"array"}},"required":["data"],"title":"WebhookEndpointsResponse","type":"object"},"BillingContact":{"description":"Billing contact information. Creates or updates a contact.","example":{"company":"Acme Corp","email":"customer@example.com","name":"John Doe","title":"Software Engineer"},"properties":{"company":{"description":"Company name","nullable":true,"type":"string"},"email":{"description":"Contact email address (required). Creates or gets a contact with this email.","format":"email","type":"string"},"name":{"description":"Contact name","nullable":true,"type":"string"},"title":{"description":"Job title","nullable":true,"type":"string"}},"required":["email"],"title":"BillingContact","type":"object"},"WebhookEndpointParams":{"description":"Parameters for creating or updating a webhook endpoint","example":{"webhook_endpoint":{"events":["invoice.created","invoice.paid","invoice.failed"],"url":"https://example.com/webhooks"}},"properties":{"webhook_endpoint":{"properties":{"active":{"description":"Whether the endpoint should be active","type":"boolean"},"events":{"description":"List of event types to subscribe to","items":{"type":"string"},"type":"array"},"url":{"description":"URL where webhook events will be sent","format":"uri","type":"string"}},"required":["url","events"],"type":"object"}},"required":["webhook_endpoint"],"title":"WebhookEndpointParams","type":"object"},"AgreementCreateParams":{"description":"Parameters for creating an agreement from a template","example":{"delivery_mode":"managed","field_values":{"field_1":"John Doe","field_2":{"content":"**MSA** for _Acme Corp_","content_type":"markdown"}},"invoice":{"amount":15000,"billing_contact":{"email":"billing@example.com","name":"Billing Contact"},"currency":"USD","memo":"Payment for services","payment_direction":"receivable","payment_methods":["card","ach"],"payment_terms_days":30,"payment_terms_type":"net"},"name":"Service Agreement","payments_enabled":false,"recipients":[{"assigned_fields":["company_address","date"],"contact_id":"770e8400-e29b-41d4-a716-446655440000","role":"owner"},{"assigned_fields":["signature_field","date_field"],"contact":{"company":"Example Corp","email":"newrecipient@example.com","name":"Jane Doe","title":"CEO"},"role":"signer"}],"reminder_schedule":"weekly","signing_order_enabled":false,"template_id":"550e8400-e29b-41d4-a716-446655440000"},"properties":{"delivery_mode":{"description":"Delivery mode: 'embedded' (emails suppressed) or 'managed' (Agree sends emails)","enum":["embedded","managed"],"type":"string"},"ends_at":{"description":"When the agreement ends (ISO8601 format)","format":"date-time","nullable":true,"type":"string"},"field_values":{"additionalProperties":{"$ref":"#/components/schemas/RichTextValue"},"description":"Map of field_id to value for prefilling fields and template variables. Values can be a legacy string or a typed rich text object. If `content_type` is omitted in object form, `plaintext` is used by default.","type":"object"},"invoice":{"description":"Invoice to create with this agreement. Creates an invoice template associated with the agreement.\n\nEither `billing_contact` or `contact_id` is required when providing an invoice.\nEither `amount`/`currency` or `line_items` is required (if line_items are provided, amount is calculated from them).\n\nIMPORTANT: `amount` and `unit_price.amount` are INTEGERS in the smallest currency unit (cents for USD), NOT dollars. A $150 invoice is `amount: 15000`. Multiply dollar amounts by 100.\n","nullable":true,"properties":{"amount":{"description":"Integer invoice amount in the smallest currency unit (cents for USD). $1.00 = 100, $15.00 = 1500, $150.00 = 15000. Do NOT pass dollars. Required if line_items is not provided.","nullable":true,"type":"integer"},"automatic_delivery":{"default":true,"description":"Send invoice automatically","nullable":true,"type":"boolean"},"automatic_payment":{"default":false,"description":"Enable automatic payment","nullable":true,"type":"boolean"},"billing_contact":{"allOf":[{"$ref":"#/components/schemas/BillingContact"}],"description":"Billing contact information. Creates or updates a contact.\nCannot be used together with contact_id.\n","nullable":true},"contact_id":{"description":"ID of an existing contact to use for this invoice.\nCannot be used together with billing_contact.\n","format":"uuid","nullable":true,"type":"string"},"currency":{"description":"ISO 4217 currency code (e.g., USD). Required if line_items is not provided.","nullable":true,"type":"string"},"forward_payment_enabled":{"default":true,"description":"Enable forward payment","nullable":true,"type":"boolean"},"issue_terms_date":{"description":"Specific date to schedule the invoice (when issue_terms_type is 'date')","format":"date-time","nullable":true,"type":"string"},"issue_terms_days":{"description":"Number of days from now to schedule the invoice (when issue_terms_type is 'net')","nullable":true,"type":"integer"},"issue_terms_type":{"description":"Issue terms type - determines when the invoice is scheduled to be sent.\n- 'net': Schedule based on days from now (use issue_terms_days)\n- 'date': Schedule for a specific date (use issue_terms_date)\nDefaults to 'net' if not provided.\n","enum":["net","date"],"nullable":true,"type":"string"},"line_items":{"description":"Invoice line items. If provided, amount is calculated from line items.","items":{"properties":{"description":{"description":"Line item description","type":"string"},"quantity":{"description":"Quantity","type":"number"},"unit_price":{"description":"Per-unit price. `amount` is an INTEGER in the smallest currency unit (cents for USD), not dollars. $25.00 = 2500.","properties":{"amount":{"description":"Integer price in the smallest currency unit (cents for USD). $25.00 = 2500, $1.00 = 100. Do NOT pass dollars.","type":"integer"},"currency":{"description":"ISO 4217 currency code","example":"USD","type":"string"}},"required":["amount","currency"],"type":"object"}},"required":["description","quantity","unit_price"],"type":"object"},"type":"array"},"memo":{"description":"Invoice memo","maxLength":255,"nullable":true,"type":"string"},"payment_direction":{"description":"Payment direction","enum":["payable","receivable"],"nullable":true,"type":"string"},"payment_methods":{"description":"Accepted payment methods","items":{"enum":["ach","card","wire"],"type":"string"},"type":"array"},"payment_terms_date":{"description":"Specific payment due date (when payment_terms_type is 'date')","format":"date-time","nullable":true,"type":"string"},"payment_terms_days":{"description":"Payment terms days","nullable":true,"type":"integer"},"payment_terms_type":{"description":"Payment terms type","enum":["net","date"],"nullable":true,"type":"string"},"recurring_end_count":{"description":"Number of occurrences when recurring_end_type is count","nullable":true,"type":"integer"},"recurring_end_date":{"description":"End date when recurring_end_type is date","format":"date-time","nullable":true,"type":"string"},"recurring_end_type":{"default":"never","description":"How the recurring invoice ends","enum":["never","date","count"],"nullable":true,"type":"string"},"reminder_schedule":{"description":"Reminder schedule for the invoice","enum":["none","daily","weekly","monthly"],"nullable":true,"type":"string"},"repeat_frequency":{"description":"How often to repeat (e.g., 1 for every week/month)","nullable":true,"type":"integer"},"repeat_on_day":{"description":"Day of month (1-31) when repeat_on_type is day_of_month","nullable":true,"type":"integer"},"repeat_on_type":{"description":"For monthly: repeat on day of month or day of week","enum":["day_of_month","day_of_week"],"nullable":true,"type":"string"},"repeat_on_week":{"description":"Week position (1-5, 5=last) when repeat_on_type is day_of_week","nullable":true,"type":"integer"},"repeat_on_weekday":{"description":"Day of week when repeat_unit is week or repeat_on_type is day_of_week","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"nullable":true,"type":"string"},"repeat_unit":{"description":"Repeat unit","enum":["week","month"],"nullable":true,"type":"string"},"sales_tax_percentage":{"description":"Sales tax percentage","nullable":true,"type":"number"},"schedule":{"default":"none","description":"Schedule type for recurring invoices","enum":["none","custom"],"nullable":true,"type":"string"}},"type":"object"},"name":{"description":"Agreement name","type":"string"},"payments_enabled":{"description":"Whether payments are enabled for this agreement","type":"boolean"},"recipients":{"description":"List of recipients with their assigned fields.\n\n**Important:** Exactly one recipient must have the `owner` role. This must be the account holder (the person whose API key is being used). Use GET /api/v1/contacts to find your Contact ID.\n\nEach recipient must provide either `contact_id` or `contact` (but not both).\n- `contact_id`: Reference an existing contact\n- `contact`: Create or update a contact with email and name (required), and optionally company and title\n\nUse `assigned_fields` to assign specific fields (by field name) to each recipient. Fields not assigned will default to the owner recipient.\n","items":{"properties":{"assigned_fields":{"description":"List of field names to assign to this recipient","items":{"type":"string"},"type":"array"},"contact":{"allOf":[{"$ref":"#/components/schemas/RecipientContact"}],"description":"Contact data to create or update a contact"},"contact_id":{"description":"Contact ID of the recipient","format":"uuid","type":"string"},"role":{"description":"Role of the recipient.\n\n**Important:** Exactly one recipient must have the `owner` role. This must be the account holder (the person whose API key is being used). The `owner` role is converted internally to `signer` or `viewer` based on assigned fields.\n","enum":["owner","signer","viewer","payee"],"type":"string"}},"required":["role"],"type":"object"},"type":"array"},"reminder_schedule":{"description":"Reminder schedule frequency (only valid for managed mode)","enum":["none","daily","weekly","monthly"],"nullable":true,"type":"string"},"signing_order":{"description":"List of contact IDs in signing order","items":{"format":"uuid","type":"string"},"type":"array"},"signing_order_enabled":{"description":"Whether signing order is enabled","type":"boolean"},"starts_at":{"description":"When the agreement starts (ISO8601 format)","format":"date-time","nullable":true,"type":"string"},"template_id":{"description":"Template ID to create agreement from (required)","format":"uuid","type":"string"}},"required":["template_id","name"],"title":"AgreementCreateParams","type":"object"},"InvoicesResponse":{"description":"Response containing a list of invoices","properties":{"data":{"description":"List of invoices","items":{"$ref":"#/components/schemas/Invoice"},"type":"array"},"pagination":{"description":"Pagination information","properties":{"page":{"description":"Current page number","type":"integer"},"page_size":{"description":"Number of items per page","type":"integer"},"total_entries":{"description":"Total number of invoices","type":"integer"},"total_pages":{"description":"Total number of pages","type":"integer"}},"required":["page","page_size","total_pages","total_entries"],"type":"object"}},"required":["data","pagination"],"title":"InvoicesResponse","type":"object"},"BadRequest":{"description":"Invalid request parameters","example":{"error":"Invalid page or page_size"},"properties":{"error":{"description":"Error message","type":"string"}},"title":"BadRequest","type":"object"},"Error":{"description":"Error response with field-specific error messages","example":{"errors":{"amount":["can't be blank"],"recurring_options":["is invalid"]}},"properties":{"errors":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"description":"Map of field names to arrays of error messages","type":"object"}},"title":"Error","type":"object"},"TemplateResponse":{"description":"Response containing a single template","properties":{"data":{"allOf":[{"$ref":"#/components/schemas/Template"}],"description":"Template data"}},"required":["data"],"title":"TemplateResponse","type":"object"},"WebhookEndpointResponse":{"description":"Response containing a single webhook endpoint","properties":{"data":{"$ref":"#/components/schemas/WebhookEndpoint"}},"required":["data"],"title":"WebhookEndpointResponse","type":"object"},"InvoicePdfPendingResponse":{"description":"PDF generation is in progress; use Retry-After and retry the same URL","properties":{"data":{"properties":{"message":{"type":"string"},"retry_after_seconds":{"description":"Same value as the Retry-After response header (seconds)","type":"integer"},"status":{"enum":["pending"],"type":"string"}},"required":["status","retry_after_seconds","message"],"type":"object"}},"required":["data"],"title":"InvoicePdfPendingResponse","type":"object"},"Customer":{"description":"The business entity (customer) billed by an invoice. Routing/billing identity is stable across contact changes; the primary contact is the invoice recipient at issue time.","example":{"business_type":"company","id":"8d1e3859-aef8-4234-97b5-3d4b89b8157c","name":"Acme Corp","primary_contact":{"email":"john@acme.com","name":"John Doe"}},"properties":{"business_type":{"description":"Customer business type","enum":["company","individual","non_profit","government_entity"],"type":"string"},"id":{"description":"Customer identifier","format":"uuid","type":"string"},"name":{"description":"Customer (company) name","type":"string"},"primary_contact":{"description":"The customer's primary contact (the invoice recipient).","nullable":true,"properties":{"email":{"description":"Primary contact email","format":"email","nullable":true,"type":"string"},"name":{"description":"Primary contact name","nullable":true,"type":"string"}},"type":"object"}},"required":["id","name"],"title":"Customer","type":"object"},"WebhookEndpoint":{"description":"A webhook endpoint configuration","example":{"active":true,"events":["invoice.created","invoice.paid"],"failure_count":0,"id":"550e8400-e29b-41d4-a716-446655440000","inserted_at":"2024-01-15T10:30:00Z","updated_at":"2024-01-15T10:30:00Z","url":"https://example.com/webhooks"},"properties":{"active":{"description":"Whether the endpoint is active","type":"boolean"},"events":{"description":"List of event types this endpoint is subscribed to","items":{"type":"string"},"type":"array"},"failure_count":{"description":"Number of consecutive delivery failures","type":"integer"},"id":{"description":"Unique webhook endpoint identifier","format":"uuid","type":"string"},"inserted_at":{"description":"When the endpoint was created","format":"date-time","type":"string"},"updated_at":{"description":"When the endpoint was last updated","format":"date-time","type":"string"},"url":{"description":"URL where webhook events will be sent","format":"uri","type":"string"}},"required":["id","url","events","active"],"title":"WebhookEndpoint","type":"object"},"InvoiceParams":{"description":"Parameters for creating or updating an invoice.\n\nOn **create**, the organization comes from the API key and an **invoice template** is created automatically from `recurring_options` (or defaults). Do not send `organization_id` or `invoice_template_id`; they are not API inputs.\n","example":{"invoice":{"amount":{"amount":15000,"currency":"USD"},"billing_contact":{"company":"Acme Corporation","email":"jane@acme.com","name":"Jane Smith"},"due_at":"2025-02-15T00:00:00Z","memo":"Consulting services - January 2025","payment_methods":["card","ach"],"recurring_options":{"recurring_end_type":"never","repeat_frequency":1,"repeat_unit":"month","schedule":"custom"},"scheduled_at":"2025-02-01T00:00:00Z"}},"properties":{"invoice":{"properties":{"agreement_id":{"description":"Associated agreement","format":"uuid","nullable":true,"type":"string"},"amount":{"allOf":[{"$ref":"#/components/schemas/Money"}],"description":"Invoice amount. Required when creating an invoice.\n\n`amount.amount` is an INTEGER in the smallest currency unit (cents for USD), not dollars. A $150 invoice is `{\"amount\": 15000, \"currency\": \"USD\"}`. If a user says \"create a $50 invoice\", send `5000`, not `50`.\n"},"automatic_delivery":{"description":"Send invoice automatically","type":"boolean"},"billing_contact":{"allOf":[{"$ref":"#/components/schemas/BillingContact"}],"description":"Billing contact information. Creates or updates a contact.\nCannot be used together with contact_id.\n","nullable":true},"contact_id":{"description":"ID of an existing contact to use for this invoice.\nCannot be used together with billing_contact.\n","format":"uuid","nullable":true,"type":"string"},"delivery_method":{"description":"Delivery method (e.g., email)","type":"string"},"due_at":{"description":"Payment due date (ISO8601 format, UTC).\nRequired when using the `create` endpoint. Optional for `create_and_send` (defaults to current UTC time if not provided).\n","format":"date-time","nullable":true,"type":"string"},"line_items":{"description":"Invoice line items","items":{"properties":{"description":{"description":"Line item description","type":"string"},"quantity":{"description":"Quantity","type":"number"},"unit_price":{"description":"Per-unit price. `amount` is an INTEGER in the smallest currency unit (cents for USD), not dollars. $25.00 = 2500.","properties":{"amount":{"description":"Integer price in the smallest currency unit (cents for USD). $25.00 = 2500, $1.00 = 100. Do NOT pass dollars.","type":"integer"},"currency":{"description":"ISO 4217 currency code","example":"USD","type":"string"}},"type":"object"}},"type":"object"},"type":"array"},"memo":{"description":"Invoice memo","maxLength":255,"nullable":true,"type":"string"},"payment_methods":{"description":"Accepted payment methods","items":{"enum":["ach","card","wire"],"type":"string"},"type":"array"},"payment_type":{"description":"Type of payment","enum":["invoice","payment","subscription"],"type":"string"},"recurring_options":{"allOf":[{"$ref":"#/components/schemas/RecurringOptions"}],"description":"Recurring options for the invoice. Configures how the invoice repeats over time.\nIf not provided, the invoice will have no recurring schedule (schedule: \"none\").\n","nullable":true},"reviewed_at":{"description":"When the invoice was reviewed/approved (ISO8601 format, UTC).\nOptional on create — defaults to the time of the API request. Invoices created via the API are\ntreated as already reviewed by the calling system, so they appear in default lists and are\neligible for the scheduler immediately. Pass an explicit value to record an earlier review time,\nor omit to accept the current-time default.\n","format":"date-time","nullable":true,"type":"string"},"sales_tax_percentage":{"description":"Sales tax percentage","nullable":true,"type":"number"},"scheduled_at":{"description":"When to send the invoice (ISO8601 format, UTC).\nRequired when using the `create` endpoint. For `create_and_send`, this is always set to the current UTC time regardless of the value provided.\n","format":"date-time","nullable":true,"type":"string"}},"type":"object"}},"required":["invoice"],"title":"InvoiceParams","type":"object"},"AgreementSendParams":{"description":"Parameters for sending an agreement","example":{"delivery_method":"email","delivery_mode":"managed","message":"Please review and sign this agreement","reminder_schedule":"weekly"},"properties":{"delivery_method":{"description":"Delivery method (only valid for managed mode)","enum":["email"],"type":"string"},"delivery_mode":{"description":"Delivery mode: 'embedded' (emails suppressed) or 'managed' (Agree sends emails)","enum":["embedded","managed"],"type":"string"},"message":{"description":"Optional message to include in email (only valid for managed mode)","nullable":true,"type":"string"},"reminder_schedule":{"description":"Reminder schedule override (only valid for managed mode)","enum":["none","daily","weekly","monthly"],"nullable":true,"type":"string"}},"required":["delivery_mode"],"title":"AgreementSendParams","type":"object"},"InvoiceResponse":{"description":"Response containing a single invoice","properties":{"data":{"$ref":"#/components/schemas/Invoice"}},"required":["data"],"title":"InvoiceResponse","type":"object"},"WebhookEndpointWithSecret":{"description":"Webhook endpoint with signing secret (only returned on creation)","properties":{"active":{"description":"Whether the endpoint is active","type":"boolean"},"events":{"description":"List of event types this endpoint is subscribed to","items":{"type":"string"},"type":"array"},"failure_count":{"description":"Number of consecutive delivery failures","type":"integer"},"id":{"description":"Unique webhook endpoint identifier","format":"uuid","type":"string"},"inserted_at":{"description":"When the endpoint was created","format":"date-time","type":"string"},"secret":{"description":"Signing secret for verifying webhook payloads. Only returned once on creation.","type":"string"},"updated_at":{"description":"When the endpoint was last updated","format":"date-time","type":"string"},"url":{"description":"URL where webhook events will be sent","format":"uri","type":"string"}},"required":["id","url","events","active","secret"],"title":"WebhookEndpointWithSecret","type":"object"},"AgreementParams":{"description":"Parameters for updating an agreement","example":{"agreement":{"ends_at":"2024-12-31T23:59:59Z","forward_signature_enabled":true,"name":"Service Agreement","payments_enabled":false,"recipients":[{"contact_id":"770e8400-e29b-41d4-a716-446655440000","role":"signer"},{"contact":{"company":"Example Corp","email":"newrecipient@example.com","name":"Jane Doe","title":"CEO"},"role":"viewer"}],"reminder_schedule":"weekly","signing_order":[],"signing_order_enabled":false,"starts_at":"2024-01-01T00:00:00Z"}},"properties":{"agreement":{"properties":{"current_signing_order":{"description":"Current position in the signing order","nullable":true,"type":"integer"},"ends_at":{"description":"When the agreement ends (ISO8601 format)","format":"date-time","nullable":true,"type":"string"},"field_values":{"additionalProperties":{"$ref":"#/components/schemas/RichTextValue"},"description":"Map of field_id to value for prefilling fields and template variables. Values can be a legacy string or a typed rich text object. If `content_type` is omitted in object form, `plaintext` is used by default.","type":"object"},"forward_signature_enabled":{"description":"Whether forward signature is enabled","type":"boolean"},"last_reminder_sent_at":{"description":"When the last reminder was sent (ISO8601 format)","format":"date-time","nullable":true,"type":"string"},"name":{"description":"Agreement name","type":"string"},"payments_enabled":{"description":"Whether payments are enabled for this agreement","type":"boolean"},"recipients":{"description":"List of recipients with their assigned fields (replaces existing recipients).\n\nEach recipient must provide either `contact_id` or `contact` (but not both).\n- `contact_id`: Reference an existing contact\n- `contact`: Create or update a contact with email and name (required), and optionally company and title\n","items":{"properties":{"assigned_fields":{"description":"List of field names to assign to this recipient","items":{"type":"string"},"type":"array"},"contact":{"allOf":[{"$ref":"#/components/schemas/RecipientContact"}],"description":"Contact data to create or update a contact"},"contact_id":{"description":"Contact ID of the recipient","format":"uuid","type":"string"},"role":{"description":"Role of the recipient","enum":["signer","viewer","payee"],"type":"string"}},"required":["role"],"type":"object"},"type":"array"},"reminder_schedule":{"description":"Reminder schedule frequency","enum":["none","daily","weekly","monthly"],"nullable":true,"type":"string"},"reminder_scheduled_at":{"description":"When the next reminder is scheduled (ISO8601 format)","format":"date-time","nullable":true,"type":"string"},"signing_order":{"description":"List of user IDs in signing order","items":{"type":"string"},"type":"array"},"signing_order_enabled":{"description":"Whether signing order is enabled","type":"boolean"},"starts_at":{"description":"When the agreement starts (ISO8601 format)","format":"date-time","nullable":true,"type":"string"}},"required":["name","starts_at"],"type":"object"}},"required":["agreement"],"title":"AgreementParams","type":"object"},"ReportDataResponse":{"description":"Report data payload","properties":{"data":{"additionalProperties":true,"description":"Report-specific payload","type":"object"}},"required":["data"],"title":"ReportDataResponse","type":"object"},"RecipientContact":{"description":"Contact data to create or update a contact","example":{"company":"Example Corp","email":"newrecipient@example.com","name":"Jane Doe","title":"CEO"},"properties":{"company":{"description":"Company name","nullable":true,"type":"string"},"email":{"description":"Email address of the contact","format":"email","type":"string"},"name":{"description":"Name of the contact","type":"string"},"title":{"description":"Job title","nullable":true,"type":"string"}},"required":["email","name"],"title":"RecipientContact","type":"object"},"NotFound":{"description":"Resource not found error","example":{"error":"Not found"},"properties":{"error":{"description":"Error message","type":"string"}},"title":"NotFound","type":"object"},"WebhookTestResponse":{"description":"Response after triggering test webhooks","example":{"data":{"failed":0,"job_ids":[123,124],"message":"Test webhooks have been queued and will be sent shortly","successful":2,"total":2}},"properties":{"data":{"properties":{"failed":{"description":"Number of failed queue attempts","type":"integer"},"job_ids":{"description":"IDs of queued webhook delivery jobs","items":{"type":"integer"},"type":"array"},"message":{"description":"Status message","type":"string"},"successful":{"description":"Number of successfully queued webhooks","type":"integer"},"total":{"description":"Total number of endpoints that received test","type":"integer"}},"type":"object"}},"required":["data"],"title":"WebhookTestResponse","type":"object"},"RichTextValue":{"description":"Either a legacy plain string or a typed rich text object. When object form omits content_type, plaintext is assumed.","example":{"content":"Renewal Date: 2026-05-01","content_type":"html"},"oneOf":[{"type":"string"},{"properties":{"content":{"description":"Raw text/html/markdown payload","type":"string"},"content_type":{"description":"Input format for content. Defaults to plaintext when omitted.","enum":["plaintext","html","markdown"],"type":"string"}},"required":["content"],"type":"object"}],"title":"RichTextValue"},"Template":{"description":"An agreement template","example":{"field_names":["signature_field","date_field","name_field"],"id":"550e8400-e29b-41d4-a716-446655440000","name":"Service Agreement Template"},"properties":{"field_names":{"description":"List of field names available in this template","items":{"type":"string"},"type":"array"},"id":{"description":"Unique template identifier","format":"uuid","type":"string"},"name":{"description":"Template name","type":"string"}},"required":["id","name","field_names"],"title":"Template","type":"object"},"AgreementResponse":{"description":"Response containing a single agreement","properties":{"data":{"$ref":"#/components/schemas/Agreement"}},"required":["data"],"title":"AgreementResponse","type":"object"},"PresignedDownloadResponse":{"description":"Time-limited URL to download a PDF from object storage","properties":{"data":{"properties":{"expires_in":{"description":"URL lifetime in seconds","type":"integer"},"url":{"description":"Presigned GET URL; expires after expires_in seconds","format":"uri","type":"string"}},"required":["url","expires_in"],"type":"object"}},"required":["data"],"title":"PresignedDownloadResponse","type":"object"},"AgreementPdfPendingResponse":{"description":"Agreement PDF generation is in progress; use Retry-After and retry the same URL","properties":{"data":{"properties":{"message":{"type":"string"},"retry_after_seconds":{"description":"Same value as the Retry-After response header (seconds)","type":"integer"},"status":{"enum":["pending"],"type":"string"}},"required":["status","retry_after_seconds","message"],"type":"object"}},"required":["data"],"title":"AgreementPdfPendingResponse","type":"object"},"ContactResponse":{"description":"Response containing a single contact","properties":{"data":{"$ref":"#/components/schemas/Contact"}},"required":["data"],"title":"ContactResponse","type":"object"},"Money":{"description":"Monetary amount with currency.\n\nIMPORTANT: `amount` is always an INTEGER in the smallest currency unit — cents for USD, EUR, GBP, etc. Never pass dollars. $1.00 is `100`, $15.00 is `1500`, $150.00 is `15000`. To convert a dollar amount to cents, multiply by 100 and round to the nearest integer.\n","example":{"amount":15000,"currency":"USD"},"properties":{"amount":{"description":"Integer amount in the smallest currency unit (cents for USD). $1.00 = 100, $15.00 = 1500, $150.00 = 15000. Do NOT pass dollars.","type":"integer"},"currency":{"description":"ISO 4217 currency code","example":"USD","type":"string"}},"required":["amount","currency"],"title":"Money","type":"object"},"Invoice":{"additionalProperties":false,"description":"An invoice for a payment request","example":{"agreement_id":null,"amount":{"amount":15000,"currency":"USD"},"authorized_at":null,"automatic_delivery":true,"billing_contact":{"company":"Acme Corporation","email":"jane@acme.com","name":"Jane Smith","title":"CFO"},"delivery_method":"email","destination_organization_id":null,"due_at":"2025-02-15T00:00:00Z","external_customer_id":null,"external_id":null,"id":"550e8400-e29b-41d4-a716-446655440000","inserted_at":"2025-01-10T14:30:00Z","invoice_url":"https://secure.agree.com/invoices?modal=invoice-details&invoiceId=550e8400-e29b-41d4-a716-446655440000","last_reminder_sent_at":null,"memo":"Consulting services - January 2025","name":"Invoice #1042","organization_id":"660e8400-e29b-41d4-a716-446655440000","paid_at":null,"payment_link":"https://agree.com/pay/abc123token","payment_methods":["card","ach"],"payment_type":"invoice","processing_at":null,"recurring_options":{"forward_payment_enabled":true,"recurring_end_type":"never","reminder_schedule":"weekly","repeat_frequency":1,"repeat_on_day":15,"repeat_on_type":"day_of_month","repeat_unit":"month","schedule":"custom"},"recurring_sequence":1,"reminder_scheduled_at":"2025-01-22T09:00:00Z","reviewed_at":"2025-01-10T14:30:00Z","sales_tax_percentage":null,"scheduled_at":"2025-01-15T09:00:00Z","sent_at":"2025-01-15T09:00:05Z","status":"sent","subscription_url":"https://secure.agree.com/subscriptions?modal=subscription-details&subscriptionId=70f2e8c2-0bc2-4f4f-8f9a-adab66f0320a","used_payment_method":null},"properties":{"agreement_id":{"description":"Associated agreement ID","format":"uuid","nullable":true,"type":"string"},"due_at":{"description":"Payment due date","format":"date-time","nullable":true,"type":"string"},"inserted_at":{"description":"When the invoice was created","format":"date-time","type":"string"},"sales_tax_percentage":{"description":"Sales tax percentage","nullable":true,"type":"number"},"delivery_method":{"description":"How the invoice is delivered","example":"email","type":"string"},"customer":{"allOf":[{"$ref":"#/components/schemas/Customer"}],"description":"Expanded customer (business entity) billed by this invoice, including its primary contact (the recipient). Nil for legacy invoices.","nullable":true},"subscription_url":{"description":"URL to navigate to the subscription in the Agree web app.\nOnly present when the invoice is part of a recurring subscription (has a recurring schedule).\nFormat: {base_url}/subscriptions?modal=subscription-details&subscriptionId={invoice_template_id}\nExamples:\n- https://secure.agree.com/subscriptions?modal=subscription-details&subscriptionId={invoice_template_id}\n","format":"uri","nullable":true,"type":"string"},"scheduled_at":{"description":"When invoice is scheduled to be sent","format":"date-time","nullable":true,"type":"string"},"customer_id":{"description":"ID of the customer (business entity) billed by this invoice. Nil for legacy invoices created before the customer entity.","format":"uuid","nullable":true,"type":"string"},"payment_type":{"description":"Type of payment","enum":["invoice","payment","subscription"],"type":"string"},"paid_at":{"description":"When payment was completed","format":"date-time","nullable":true,"type":"string"},"payment_methods":{"description":"Accepted payment methods","items":{"enum":["ach","card","wire"],"type":"string"},"type":"array"},"automatic_delivery":{"description":"Whether invoice is sent automatically","type":"boolean"},"recurring_options":{"allOf":[{"$ref":"#/components/schemas/RecurringOptions"}],"description":"Recurring options for this invoice. Nil if invoice has no recurring schedule.","nullable":true},"reviewed_at":{"description":"When the invoice was reviewed/approved. Invoices with `reviewed_at = null` are hidden from default lists and skipped by the scheduler.","format":"date-time","nullable":true,"type":"string"},"status":{"description":"Current invoice status","enum":["created","due","sent","canceled","paid","failed","refunded","draft"],"type":"string"},"sent_at":{"description":"When invoice was sent","format":"date-time","nullable":true,"type":"string"},"payment_link":{"description":"URL for the payment page. Only available when invoice has a payment token.","format":"uri","nullable":true,"type":"string"},"name":{"description":"Invoice name","nullable":true,"type":"string"},"id":{"description":"Unique invoice identifier","format":"uuid","type":"string"},"billing_contact":{"allOf":[{"$ref":"#/components/schemas/BillingContact"}],"description":"Billing contact information for this invoice","nullable":true},"destination_organization_id":{"description":"Destination organization ID","format":"uuid","nullable":true,"type":"string"},"processing_at":{"description":"When processing started","format":"date-time","nullable":true,"type":"string"},"used_payment_method":{"description":"Payment method used for payment","nullable":true,"type":"string"},"external_id":{"description":"External invoice reference","nullable":true,"type":"string"},"invoice_url":{"description":"URL to navigate to the invoice in the Agree web app.\nFormat: {base_url}/invoices?modal=invoice-details&invoiceId={invoice_id}\nExamples:\n- https://secure.agree.com/invoices?modal=invoice-details&invoiceId={invoice_id}\n","format":"uri","nullable":true,"type":"string"},"external_customer_id":{"description":"External customer reference","nullable":true,"type":"string"},"last_reminder_sent_at":{"description":"When last reminder was sent","format":"date-time","nullable":true,"type":"string"},"memo":{"description":"Invoice memo or notes","maxLength":255,"nullable":true,"type":"string"},"recurring_sequence":{"description":"Sequence number for recurring invoices","type":"integer"},"amount":{"$ref":"#/components/schemas/Money"},"reminder_scheduled_at":{"description":"When next reminder is scheduled","format":"date-time","nullable":true,"type":"string"},"organization_id":{"description":"Organization that owns this invoice","format":"uuid","type":"string"},"authorized_at":{"description":"When payment was authorized","format":"date-time","nullable":true,"type":"string"}},"required":["id","amount","status","organization_id"],"title":"Invoice","type":"object"},"Agreement":{"description":"An agreement in the system","example":{"current_signing_order":0,"deleted_at":null,"delivery_mode":"managed","docs_url":"https://secure.agree.com/docs/550e8400-e29b-41d4-a716-446655440000","ends_at":"2024-12-31T23:59:59Z","executed_at":null,"forward_signature_enabled":true,"id":"550e8400-e29b-41d4-a716-446655440000","invoice_template_id":null,"last_reminder_sent_at":null,"name":"Service Agreement","organization_id":"660e8400-e29b-41d4-a716-446655440000","payments_enabled":false,"preview_url":"https://example.com/preview/550e8400","reminder_schedule":"weekly","reminder_scheduled_at":"2024-01-22T10:00:00Z","share_url":null,"signers":[{"assigned_fields":["signature_field","date_field"],"contact_id":"770e8400-e29b-41d4-a716-446655440000","role":"signer","signing_link":"https://example.com/sign/abc123token","status":"pending"}],"signing_order":[],"signing_order_enabled":false,"starts_at":"2024-01-01T00:00:00Z","status":"drafted","version":0},"properties":{"current_signing_order":{"description":"Current position in the signing order","nullable":true,"type":"integer"},"deleted_at":{"description":"When the agreement was deleted (soft delete)","format":"date-time","nullable":true,"type":"string"},"delivery_mode":{"description":"Delivery mode: 'embedded' (emails suppressed) or 'managed' (Agree sends emails)","enum":["embedded","managed"],"type":"string"},"docs_url":{"description":"URL to navigate to the agreement in the Agree web app.\nFormat: {base_url}/docs/{agreement_id}\nExamples:\n- https://secure.agree.com/docs/{agreement_id}\n","format":"uri","nullable":true,"type":"string"},"ends_at":{"description":"When the agreement ends","format":"date-time","nullable":true,"type":"string"},"executed_at":{"description":"When the agreement was executed","format":"date-time","nullable":true,"type":"string"},"forward_signature_enabled":{"description":"Whether forward signature is enabled","type":"boolean"},"id":{"description":"Unique agreement identifier","format":"uuid","type":"string"},"invoice_template_id":{"description":"ID of the invoice template linked to this agreement, when created with an invoice","format":"uuid","nullable":true,"type":"string"},"last_reminder_sent_at":{"description":"When the last reminder was sent","format":"date-time","nullable":true,"type":"string"},"name":{"description":"Agreement name","type":"string"},"organization_id":{"description":"Organization that owns this agreement","format":"uuid","nullable":true,"type":"string"},"payments_enabled":{"description":"Whether payments are enabled for this agreement","type":"boolean"},"preview_url":{"description":"URL to preview the agreement","nullable":true,"type":"string"},"reminder_schedule":{"description":"Reminder schedule frequency","enum":["none","daily","weekly","monthly"],"nullable":true,"type":"string"},"reminder_scheduled_at":{"description":"When the next reminder is scheduled","format":"date-time","nullable":true,"type":"string"},"share_url":{"description":"Share URL (generic share link) if enabled","format":"uri","nullable":true,"type":"string"},"signers":{"description":"List of signers with their assigned fields","items":{"$ref":"#/components/schemas/Signer"},"type":"array"},"signing_order":{"description":"List of user IDs in signing order","items":{"type":"string"},"type":"array"},"signing_order_enabled":{"description":"Whether signing order is enabled","type":"boolean"},"starts_at":{"description":"When the agreement starts","format":"date-time","nullable":true,"type":"string"},"status":{"description":"Agreement status","enum":["created","drafted","executed","renewed","sent","signed","terminated","viewed"],"type":"string"},"version":{"description":"Agreement version number","type":"integer"}},"required":["id","name","status","version","signers"],"title":"Agreement","type":"object"},"Signer":{"description":"A signer with their assigned fields","example":{"assigned_fields":["signature_field","date_field"],"contact_id":"770e8400-e29b-41d4-a716-446655440000","email":"john.doe@example.com","name":"John Doe","role":"signer","signing_link":"https://example.com/sign/abc123token","status":"pending"},"properties":{"assigned_fields":{"description":"List of field names assigned to this signer","items":{"type":"string"},"type":"array"},"contact_id":{"description":"Contact ID of the signer","format":"uuid","type":"string"},"email":{"description":"Contact's email address","format":"email","nullable":true,"type":"string"},"name":{"description":"Contact's name","nullable":true,"type":"string"},"role":{"description":"Role of the signer","enum":["payee","sender","signer","viewer","viewer_hidden"],"type":"string"},"signing_link":{"description":"URL to sign the agreement (null if user is not available or token cannot be created)","format":"uri","nullable":true,"type":"string"},"status":{"description":"Status of the signer","enum":["paid","pending","sent","signed","viewed"],"type":"string"}},"required":["contact_id","assigned_fields","role","status"],"title":"Signer","type":"object"},"ContactParams":{"description":"Parameters for creating or updating a contact","example":{"contact":{"company":"Acme Inc","email":"john.doe@example.com","name":"John Doe","title":"CEO"}},"properties":{"contact":{"properties":{"address":{"description":"Contact's mailing address","nullable":true,"type":"string"},"company":{"description":"Contact's company name","nullable":true,"type":"string"},"email":{"description":"Contact's email address","format":"email","type":"string"},"name":{"description":"Contact's full name","type":"string"},"title":{"description":"Contact's job title","nullable":true,"type":"string"}},"required":["name","email"],"type":"object"}},"required":["contact"],"title":"ContactParams","type":"object"},"TemplatesResponse":{"description":"Response containing a list of templates","properties":{"data":{"description":"List of templates","items":{"$ref":"#/components/schemas/Template"},"type":"array"}},"required":["data"],"title":"TemplatesResponse","type":"object"},"WebhookEndpointCreatedResponse":{"description":"Response containing a newly created webhook endpoint with signing secret","properties":{"data":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}},"required":["data"],"title":"WebhookEndpointCreatedResponse","type":"object"},"AgreementsResponse":{"description":"Response containing a list of agreements","properties":{"data":{"description":"List of agreements","items":{"$ref":"#/components/schemas/Agreement"},"type":"array"},"pagination":{"description":"Pagination information","properties":{"page":{"description":"Current page number","type":"integer"},"page_size":{"description":"Number of items per page","type":"integer"},"total_entries":{"description":"Total number of agreements","type":"integer"},"total_pages":{"description":"Total number of pages","type":"integer"}},"required":["page","page_size","total_pages","total_entries"],"type":"object"}},"required":["data","pagination"],"title":"AgreementsResponse","type":"object"},"Forbidden":{"description":"Access denied to the requested resource","example":{"error":"You do not have access to this resource"},"properties":{"error":{"description":"Error message","type":"string"}},"title":"Forbidden","type":"object"},"ReportListDataResponse":{"description":"Report data payload containing a list","properties":{"data":{"description":"List of report records","items":{"additionalProperties":true,"type":"object"},"type":"array"}},"required":["data"],"title":"ReportListDataResponse","type":"object"},"RecurringOptions":{"description":"Recurring invoice options. Configures how the invoice repeats over time.\n","example":{"forward_payment_enabled":true,"pass_on_fees_enabled":false,"recurring_end_type":"never","reminder_schedule":"none","repeat_frequency":1,"repeat_on_day":15,"repeat_on_type":"day_of_month","repeat_unit":"month","schedule":"custom"},"properties":{"forward_payment_enabled":{"default":true,"description":"Enable forward payment","type":"boolean"},"pass_on_fees_enabled":{"default":false,"description":"Pass processing fees to the payer at checkout","type":"boolean"},"recurring_end_count":{"description":"Number of occurrences when recurring_end_type is count","nullable":true,"type":"integer"},"recurring_end_date":{"description":"End date when recurring_end_type is date","format":"date-time","nullable":true,"type":"string"},"recurring_end_type":{"default":"never","description":"How the recurring invoice ends","enum":["never","date","count"],"type":"string"},"reminder_schedule":{"default":"none","description":"Reminder schedule","enum":["none","daily","weekly","monthly"],"type":"string"},"repeat_frequency":{"default":1,"description":"How often to repeat (e.g., 1 for every week/month)","type":"integer"},"repeat_on_day":{"description":"Day of month (1-31) when repeat_on_type is day_of_month","nullable":true,"type":"integer"},"repeat_on_type":{"description":"For monthly: repeat on day of month or day of week","enum":["day_of_month","day_of_week"],"nullable":true,"type":"string"},"repeat_on_week":{"description":"Week position (1-5, 5=last) when repeat_on_type is day_of_week","nullable":true,"type":"integer"},"repeat_on_weekday":{"description":"Day of week when repeat_unit is week or repeat_on_type is day_of_week","enum":["monday","tuesday","wednesday","thursday","friday","saturday","sunday"],"nullable":true,"type":"string"},"repeat_unit":{"default":"month","description":"Repeat unit","enum":["week","month"],"type":"string"},"schedule":{"default":"none","description":"Schedule type","enum":["none","custom"],"type":"string"}},"title":"RecurringOptions","type":"object"},"ContactsResponse":{"description":"Response containing a list of contacts","properties":{"data":{"description":"List of contacts","items":{"$ref":"#/components/schemas/Contact"},"type":"array"},"pagination":{"description":"Pagination information","properties":{"page":{"description":"Current page number","type":"integer"},"page_size":{"description":"Number of items per page","type":"integer"},"total_entries":{"description":"Total number of contacts","type":"integer"},"total_pages":{"description":"Total number of pages","type":"integer"}},"required":["page","page_size","total_pages","total_entries"],"type":"object"}},"required":["data","pagination"],"title":"ContactsResponse","type":"object"},"Unauthorized":{"description":"Authentication required or invalid credentials","example":{"error":"Invalid or missing API key"},"properties":{"error":{"description":"Error message","type":"string"}},"title":"Unauthorized","type":"object"},"Contact":{"description":"A contact in the organization's address book","example":{"address":"123 Main St, New York, NY 10001","company":"Acme Inc","email":"john.doe@example.com","id":"550e8400-e29b-41d4-a716-446655440000","inserted_at":"2024-01-15T10:30:00Z","name":"John Doe","organization_id":"660e8400-e29b-41d4-a716-446655440000","title":"CEO","updated_at":"2024-01-15T10:30:00Z"},"properties":{"address":{"description":"Contact's mailing address","nullable":true,"type":"string"},"company":{"description":"Contact's company name","nullable":true,"type":"string"},"email":{"description":"Contact's email address","format":"email","type":"string"},"id":{"description":"Unique contact identifier","format":"uuid","type":"string"},"inserted_at":{"description":"When the contact was created","format":"date-time","type":"string"},"name":{"description":"Contact's full name","type":"string"},"organization_id":{"description":"Organization that owns this contact","format":"uuid","type":"string"},"title":{"description":"Contact's job title","nullable":true,"type":"string"},"updated_at":{"description":"When the contact was last updated","format":"date-time","type":"string"}},"required":["id","name","email","organization_id"],"title":"Contact","type":"object"}},"securitySchemes":{"bearer":{"description":"API key authentication via Bearer token","scheme":"bearer","type":"http"}}},"info":{"description":"# Introduction\n\nWelcome to the Agree API! Agree is a payments and agreements platform that helps businesses send invoices, collect payments, and manage customer relationships. This API lets you integrate Agree's capabilities directly into your application.\n\n## What You Can Do\n\nWith the Agree API, you can:\n\n- **Create and send invoices** to customers with flexible payment options (ACH, credit card, wire transfer)\n- **Manage contacts** in your organization's address book\n- **Set up recurring invoices** with customizable schedules\n- **Receive real-time notifications** via webhooks when payments are made or invoices change status\n- **Track payment status** and history programmatically\n\n## Quick Start\n\nHere's how to send your first invoice in three API calls:\n\n### 1. Get Your API Key\n\nGenerate an API key in your Agree dashboard under **Settings > API Keys**. Keep this key secure - it provides full access to your organization's data.\n\n### 2. Create a Contact\n\nFirst, add the customer you want to invoice:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/contacts \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"contact\": {\n      \"name\": \"Jane Smith\",\n      \"email\": \"jane@example.com\",\n      \"company\": \"Acme Corp\"\n    }\n  }'\n```\n\n### 3. Create and Send an Invoice\n\nNow create and send an invoice for that contact. You can use the convenience endpoint to do both in one request:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/invoices/create_and_send \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"invoice\": {\n      \"billing_contact\": {\n        \"email\": \"jane@example.com\"\n      },\n      \"amount\": {\"amount\": 10000, \"currency\": \"USD\"},\n      \"payment_methods\": [\"card\", \"ach\"],\n      \"due_at\": \"2025-02-01T00:00:00Z\",\n      \"memo\": \"Consulting services - January 2025\"\n    }\n  }'\n```\n\nThe invoice will be created and immediately sent to the customer via email. They'll receive a payment link where they can pay using their preferred method.\n\n**Alternative:** You can also create the invoice first with `POST /api/v1/invoices`, then send it later with `POST /api/v1/invoices/:id/send` if you need to review or modify it before sending.\n\n### 4. Get Notified When They Pay\n\nSet up a webhook to know when the invoice is paid:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/webhook_endpoints \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"webhook_endpoint\": {\n      \"url\": \"https://your-app.com/webhooks/agree\",\n      \"events\": [\"invoice.paid\", \"invoice.failed\"]\n    }\n  }'\n```\n\nSave the `secret` from the response - you'll need it to verify webhook signatures.\n\n## How Resources Connect\n\nUnderstanding how Agree's resources relate to each other will help you build effective integrations:\n\n```\nContacts ──────► Invoices ──────► Webhooks\n   │                │                 │\n   │                │                 │\n   ▼                ▼                 ▼\n People you      Payment           Real-time\n do business     requests          notifications\n with            you send          when things\n                 to contacts       happen\n```\n\n- **Contacts** are people or companies in your address book. You can create contacts explicitly, or they're created automatically when you send an invoice to a new email address.\n\n- **Invoices** are payment requests sent to contacts. Each invoice has an amount, due date, and accepted payment methods. Invoices progress through a lifecycle from created → sent → paid (or failed/canceled).\n\n- **Webhooks** notify your application when invoice events occur. This lets you update your systems in real-time without polling.\n\n## Base URL\n\nAll API requests should be made to:\n\n```\nhttps://api.agree.com/api/v1\n```\n\n## Authentication\n\nAll endpoints require Bearer token authentication. Include your API key in the Authorization header:\n\n```\nAuthorization: Bearer YOUR_API_KEY\n```\n\nKeep your API key secure. If compromised, regenerate it immediately in your dashboard.\n\n## Request Format\n\nSend request bodies as JSON with the `Content-Type: application/json` header.\n\n## Response Format\n\nAll successful responses return JSON with a `data` wrapper:\n\n```json\n{\n  \"data\": { ... }\n}\n```\n\nList endpoints include pagination information:\n\n```json\n{\n  \"data\": [ ... ],\n  \"pagination\": {\n    \"page\": 1,\n    \"page_size\": 10,\n    \"total_pages\": 5,\n    \"total_entries\": 42\n  }\n}\n```\n\n## Errors\n\nErrors return appropriate HTTP status codes with details:\n\n| Status | Description |\n|--------|-------------|\n| 400 | Bad Request - Invalid parameters |\n| 401 | Unauthorized - Invalid or missing API key |\n| 403 | Forbidden - No access to this resource |\n| 404 | Not Found - Resource doesn't exist |\n| 422 | Unprocessable Entity - Validation errors |\n\nError responses include field-specific messages:\n\n```json\n{\n  \"errors\": {\n    \"email\": [\"has already been taken\"],\n    \"amount\": [\"must be greater than 0\"]\n  }\n}\n```\n\n## Pagination\n\nList endpoints support pagination with these query parameters:\n\n| Parameter | Default | Description |\n|-----------|---------|-------------|\n| `page` | 1 | Page number to retrieve |\n| `page_size` | 10 | Number of items per page (max: 100) |\n\n## Need Help?\n\n- Check the endpoint documentation below for detailed parameter information\n- Test webhooks using the `webhook.test` event\n- Contact support@agree.com for API assistance\n","title":"Agree API","version":"1.0.0"},"openapi":"3.0.0","paths":{"/api/v1/agreements/{id}/pdf":{"get":{"callbacks":{},"description":"Returns a presigned URL to download the agreement PDF for the **current revision** (same source as the Agree app document menu).\n\nIf the PDF is not in storage yet or is stale vs. the revision, the server first waits briefly in case another request already\nkicked off generation, then may enqueue SSR rendering and waits up to a **short inline budget**\n(default 5 seconds, `invoice_pdf_api_inline_wait_ms`). If the file is still not ready, responds\nwith **202 Accepted**, a `Retry-After` header (default 3 seconds, `invoice_pdf_api_retry_after_seconds`),\nand `data.status: \"pending\"`. **Repeat the same GET** until you receive **200** with `data.url`.\n","operationId":"AgreeWeb.API.V1.AgreementController.pdf","parameters":[{"description":"Agreement ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignedDownloadResponse"}}},"description":"Presigned download URL"},"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgreementPdfPendingResponse"}}},"description":"PDF not ready; retry after Retry-After"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequest"}}},"description":"Bad request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Download agreement PDF","tags":["Agreements"]}},"/api/v1/invoices/{id}/receipt_pdf":{"get":{"callbacks":{},"description":"Returns a presigned URL to download the payment receipt PDF for a **paid** invoice\n(same as the Agree app invoice menu). Returns 422 if the invoice is not paid.\n","operationId":"AgreeWeb.API.V1.InvoiceController.receipt_pdf","parameters":[{"description":"Invoice ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignedDownloadResponse"}}},"description":"Presigned download URL"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequest"}}},"description":"Bad request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Receipt PDF not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequest"}}},"description":"Invoice not paid"}},"security":[{"bearer":[]}],"summary":"Download receipt PDF","tags":["Invoices"]}},"/api/v1/reports/revenue/stats":{"get":{"callbacks":{},"description":"Returns ARR, MRR, MRR growth rate, net revenue retention (NRR), and top-5 customer concentration.\n","operationId":"AgreeWeb.API.V1.ReportController.revenue_stats","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDataResponse"}}},"description":"Revenue statistics"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Revenue statistics","tags":["Reports"]}},"/api/v1/agreements/{id}/send":{"post":{"callbacks":{},"description":"Sends an agreement to its recipients. Updates the agreement status to 'sent' and sends emails if delivery_mode is 'managed'.","operationId":"AgreeWeb.API.V1.AgreementController.send","parameters":[{"description":"Agreement ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgreementSendParams"}}},"description":"Send params","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgreementResponse"}}},"description":"Agreement sent"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Bad request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Send agreement","tags":["Agreements"]}},"/api/v1/reports/recovery/aging/trend":{"get":{"callbacks":{},"description":"Returns total overdue amount and average days overdue per month.","operationId":"AgreeWeb.API.V1.ReportController.recovery_aging_trend","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportListDataResponse"}}},"description":"Aging trend"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Aging trend","tags":["Reports"]}},"/api/v1/agreements/create_and_send":{"post":{"callbacks":{},"description":"Convenience endpoint that creates an agreement from a template and sends it immediately.\n\nCombines create and send operations in a single request.\n","operationId":"AgreeWeb.API.V1.AgreementController.create_and_send","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgreementCreateParams"}}},"description":"Create and send params","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgreementResponse"}}},"description":"Agreement created and sent"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Template not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Create and send agreement","tags":["Agreements"]}},"/api/v1/webhooks":{"get":{"callbacks":{},"description":"Returns all webhook endpoints for the authenticated organization.","operationId":"AgreeWeb.API.V1.WebhookEndpointController.index","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointsResponse"}}},"description":"Webhook endpoints list"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"List webhook endpoints","tags":["Webhooks"]},"post":{"callbacks":{},"description":"Creates a new webhook endpoint for the authenticated organization.\n\n**Important:** The signing `secret` is only returned once in the response when the endpoint is created.\nStore it securely as it cannot be retrieved again.\n\nUse the secret to verify webhook signatures by computing an HMAC-SHA256 of the request body\nand comparing it to the `X-Agree-Signature` header.\n","operationId":"AgreeWeb.API.V1.WebhookEndpointController.create","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointParams"}}},"description":"Webhook endpoint params","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointCreatedResponse"}}},"description":"Webhook endpoint created"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Create webhook endpoint","tags":["Webhooks"]}},"/api/v1/invoices/{id}/mark_as_sent":{"post":{"callbacks":{},"description":"Marks an invoice as sent with optional sent date.","operationId":"AgreeWeb.API.V1.InvoiceController.mark_as_sent","parameters":[{"description":"Invoice ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"sent_at":{"description":"Date when invoice was sent (ISO8601 format: YYYY-MM-DD). Defaults to today.","format":"date","type":"string"}},"type":"object"}}},"description":"Mark as sent params","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}},"description":"Invoice marked as sent"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Mark invoice as sent","tags":["Invoices"]}},"/api/v1/contacts":{"get":{"callbacks":{},"description":"Returns a paginated list of contacts for the authenticated organization.","operationId":"AgreeWeb.API.V1.ContactController.index","parameters":[{"description":"Page number (default: 1)","in":"query","name":"page","required":false,"schema":{"type":"integer"}},{"description":"Items per page (default: 10)","in":"query","name":"page_size","required":false,"schema":{"type":"integer"}},{"description":"Filter by email (fuzzy search)","in":"query","name":"email","required":false,"schema":{"type":"string"}},{"description":"Filter by company name (fuzzy search)","in":"query","name":"company","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactsResponse"}}},"description":"Contacts list"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequest"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"List contacts","tags":["Contacts"]},"post":{"callbacks":{},"description":"Creates a new contact for the authenticated organization.\n\nIf a user with the provided email doesn't exist, one will be created automatically.\nThe email must be unique within the organization's contacts.\n","operationId":"AgreeWeb.API.V1.ContactController.create","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactParams"}}},"description":"Contact params","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactResponse"}}},"description":"Contact created"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Create contact","tags":["Contacts"]}},"/api/v1/reports/revenue/chart":{"get":{"callbacks":{},"description":"Returns historical and forecast MRR/ARR by month.","operationId":"AgreeWeb.API.V1.ReportController.revenue_chart","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportListDataResponse"}}},"description":"Revenue chart"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Revenue chart data","tags":["Reports"]}},"/api/v1/invoices/{id}/send":{"post":{"callbacks":{},"description":"Sends an invoice to its recipient. Schedules the invoice for sending via the invoice scheduler,\nwhich will send email notifications to the recipient.\n","operationId":"AgreeWeb.API.V1.InvoiceController.send","parameters":[{"description":"Invoice ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}},"description":"Invoice sent"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Bad request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Send invoice","tags":["Invoices"]}},"/api/v1/reports/recovery/leakage/stage_durations":{"get":{"callbacks":{},"description":"Returns average days between invoice stages for current vs previous six-month periods.","operationId":"AgreeWeb.API.V1.ReportController.recovery_leakage_stage_durations","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDataResponse"}}},"description":"Stage durations"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Stage duration statistics","tags":["Reports"]}},"/api/v1/reports/recovery/leakage/stats":{"get":{"callbacks":{},"description":"Returns invoice counts and amounts by stage (drafted, outstanding, overdue, paid).","operationId":"AgreeWeb.API.V1.ReportController.recovery_leakage_stats","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDataResponse"}}},"description":"Leakage statistics"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Leakage statistics","tags":["Reports"]}},"/api/v1/reports/recovery/leakage/waterfall":{"get":{"callbacks":{},"description":"Returns waterfall stages for invoice progression (drafted → outstanding → overdue → paid).","operationId":"AgreeWeb.API.V1.ReportController.recovery_leakage_waterfall","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportListDataResponse"}}},"description":"Leakage waterfall"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Leakage waterfall","tags":["Reports"]}},"/api/v1/reports/cashflow/chart":{"get":{"callbacks":{},"description":"Returns historical and forecast cash collected by month.","operationId":"AgreeWeb.API.V1.ReportController.cashflow_chart","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportListDataResponse"}}},"description":"Cashflow chart"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Cashflow chart data","tags":["Reports"]}},"/api/v1/reports/recovery/leakage/stalled_invoices":{"get":{"callbacks":{},"description":"Lists invoices that have been stalled in a workflow stage.","operationId":"AgreeWeb.API.V1.ReportController.recovery_leakage_stalled_invoices","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportListDataResponse"}}},"description":"Stalled invoices"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Stalled invoices","tags":["Reports"]}},"/api/v1/reports/cashflow/forecast":{"get":{"callbacks":{},"description":"Returns projected cash from outstanding invoices due in the next 30, 60, and 90 days.","operationId":"AgreeWeb.API.V1.ReportController.cashflow_forecast","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDataResponse"}}},"description":"Cash forecast"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Cash forecast","tags":["Reports"]}},"/api/v1/invoices":{"get":{"callbacks":{},"description":"Returns a paginated list of invoices for the authenticated organization.","operationId":"AgreeWeb.API.V1.InvoiceController.index","parameters":[{"description":"Page number (default: 1)","in":"query","name":"page","required":false,"schema":{"type":"integer"}},{"description":"Items per page (default: 10)","in":"query","name":"page_size","required":false,"schema":{"type":"integer"}},{"description":"Start date for filtering (ISO8601 format: YYYY-MM-DD)","in":"query","name":"date_start","required":false,"schema":{"type":"string"}},{"description":"End date for filtering (ISO8601 format: YYYY-MM-DD)","in":"query","name":"date_end","required":false,"schema":{"type":"string"}},{"description":"Date field to filter by. Valid values: paid_at, due_at, scheduled_at (default: scheduled_at)","in":"query","name":"date_type","required":false,"schema":{"type":"string"}},{"description":"Timezone for date filtering (default: Etc/UTC)","in":"query","name":"date_timezone","required":false,"schema":{"type":"string"}},{"description":"Filter by invoice statuses (comma-separated). Valid values: created, due, sent, canceled, paid, failed, refunded, draft","in":"query","name":"statuses","required":false,"schema":{"type":"string"}},{"description":"Minimum invoice amount (in dollars)","in":"query","name":"amount_min","required":false,"schema":{"type":"number"}},{"description":"Maximum invoice amount (in dollars)","in":"query","name":"amount_max","required":false,"schema":{"type":"number"}},{"description":"Filter by customer/company name (fuzzy search)","in":"query","name":"customer","required":false,"schema":{"type":"string"}},{"description":"Include draft invoices (invoice templates without invoices)","in":"query","name":"include_drafts","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoicesResponse"}}},"description":"Invoices list"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"List invoices","tags":["Invoices"]},"post":{"callbacks":{},"description":"Creates a new invoice for the authenticated organization.\n\nAmounts are integers in the smallest currency unit (cents for USD), NOT dollars. A $150 invoice is `{\"amount\": 15000, \"currency\": \"USD\"}`. Multiply dollar amounts by 100.\n\nRequired fields:\n- `due_at` - Payment due date (ISO8601 format, UTC)\n- `scheduled_at` - When to send the invoice (ISO8601 format, UTC)\n\nYou can optionally provide:\n- `recurring_options` to configure recurring settings. If not provided, the invoice will have no recurring schedule (schedule: \"none\").\n\nYou can optionally provide either:\n- `billing_contact` with `email` and `name` (and optionally `company` and `title`) to automatically create or update a contact\n- `contact_id` to reference an existing contact\n\nBoth will set the invoice's `billing_contact` automatically. You cannot provide both.\n","operationId":"AgreeWeb.API.V1.InvoiceController.create","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceParams"}}},"description":"Invoice params","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}},"description":"Invoice created"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Create invoice","tags":["Invoices"]}},"/api/v1/invoices/{id}/pdf":{"get":{"callbacks":{},"description":"Returns a presigned URL to download the invoice PDF (same source as the Agree app invoice menu).\n\nIf the PDF is not in storage yet, the server first waits briefly in case another request already\nkicked off generation, then may enqueue generation and waits up to a **short inline budget**\n(default 5 seconds, `invoice_pdf_api_inline_wait_ms`). If the file is still not ready, responds\nwith **202 Accepted**, a `Retry-After` header (default 3 seconds, `invoice_pdf_api_retry_after_seconds`),\nand `data.status: \"pending\"`. **Repeat the same GET** until you receive **200** with `data.url`.\n","operationId":"AgreeWeb.API.V1.InvoiceController.pdf","parameters":[{"description":"Invoice ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignedDownloadResponse"}}},"description":"Presigned download URL"},"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoicePdfPendingResponse"}}},"description":"PDF not ready; retry after Retry-After"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequest"}}},"description":"Bad request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Download invoice PDF","tags":["Invoices"]}},"/api/v1/reports/revenue/customers_by_mrr":{"get":{"callbacks":{},"description":"Lists customers ranked by current monthly recurring revenue from active subscriptions.","operationId":"AgreeWeb.API.V1.ReportController.revenue_customers_by_mrr","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportListDataResponse"}}},"description":"Customers by MRR"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Customers by MRR","tags":["Reports"]}},"/api/v1/reports/cashflow/outstanding_invoices":{"get":{"callbacks":{},"description":"Lists outstanding (sent or due) invoices with customer and aging details.","operationId":"AgreeWeb.API.V1.ReportController.cashflow_outstanding_invoices","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportListDataResponse"}}},"description":"Outstanding invoices"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Outstanding invoices","tags":["Reports"]}},"/api/v1/agreements/templates":{"get":{"callbacks":{},"description":"Returns a list of agreement templates for the authenticated organization with field names.","operationId":"AgreeWeb.API.V1.AgreementController.templates","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplatesResponse"}}},"description":"Templates list"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"List agreement templates","tags":["Agreements"]}},"/api/v1/contacts/{id}":{"delete":{"callbacks":{},"description":"Deletes a contact by ID.","operationId":"AgreeWeb.API.V1.ContactController.delete","parameters":[{"description":"Contact ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Contact deleted"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Delete contact","tags":["Contacts"]},"get":{"callbacks":{},"description":"Returns a single contact by ID.","operationId":"AgreeWeb.API.V1.ContactController.show","parameters":[{"description":"Contact ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactResponse"}}},"description":"Contact"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Get contact","tags":["Contacts"]},"patch":{"callbacks":{},"description":"Updates an existing contact.","operationId":"AgreeWeb.API.V1.ContactController.update (2)","parameters":[{"description":"Contact ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactParams"}}},"description":"Contact params","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactResponse"}}},"description":"Contact updated"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Update contact","tags":["Contacts"]},"put":{"callbacks":{},"description":"Updates an existing contact.","operationId":"AgreeWeb.API.V1.ContactController.update","parameters":[{"description":"Contact ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactParams"}}},"description":"Contact params","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactResponse"}}},"description":"Contact updated"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Update contact","tags":["Contacts"]}},"/api/v1/invoices/{id}":{"delete":{"callbacks":{},"description":"Deletes an invoice by ID.","operationId":"AgreeWeb.API.V1.InvoiceController.delete","parameters":[{"description":"Invoice ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Invoice deleted"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Delete invoice","tags":["Invoices"]},"get":{"callbacks":{},"description":"Returns a single invoice by ID.","operationId":"AgreeWeb.API.V1.InvoiceController.show","parameters":[{"description":"Invoice ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}},"description":"Invoice"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Get invoice","tags":["Invoices"]},"patch":{"callbacks":{},"description":"Updates an existing invoice.","operationId":"AgreeWeb.API.V1.InvoiceController.update (2)","parameters":[{"description":"Invoice ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceParams"}}},"description":"Invoice params","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}},"description":"Invoice updated"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Update invoice","tags":["Invoices"]},"put":{"callbacks":{},"description":"Updates an existing invoice.","operationId":"AgreeWeb.API.V1.InvoiceController.update","parameters":[{"description":"Invoice ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceParams"}}},"description":"Invoice params","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}},"description":"Invoice updated"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Update invoice","tags":["Invoices"]}},"/api/v1/reports/cashflow/stats":{"get":{"callbacks":{},"description":"Returns cash collected month-to-date, outstanding invoice total, days sales outstanding (DSO),\nand payment failure rate.\n","operationId":"AgreeWeb.API.V1.ReportController.cashflow_stats","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDataResponse"}}},"description":"Cashflow statistics"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Cashflow statistics","tags":["Reports"]}},"/api/v1/reports/revenue/customers":{"get":{"callbacks":{},"description":"Lists customers ranked by total paid invoice revenue.","operationId":"AgreeWeb.API.V1.ReportController.revenue_customers","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportListDataResponse"}}},"description":"Customers by revenue"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Customers by revenue","tags":["Reports"]}},"/api/v1/agreements":{"get":{"callbacks":{},"description":"Returns a paginated list of agreements for the authenticated organization.","operationId":"AgreeWeb.API.V1.AgreementController.index","parameters":[{"description":"Page number (default: 1)","in":"query","name":"page","required":false,"schema":{"type":"integer"}},{"description":"Items per page (default: 10)","in":"query","name":"page_size","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgreementsResponse"}}},"description":"Agreements list"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequest"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"List agreements","tags":["Agreements"]},"post":{"callbacks":{},"description":"Creates a new agreement from a template for the authenticated organization.\n\nThe agreement will be created with status 'drafted' by default.\nRequires a template_id and supports prefilling fields via field_values mapping.\n\n**Important:** Exactly one recipient must have the `owner` role. This must be the account holder (the person whose API key is being used). The account holder is also a contact - use GET /api/v1/contacts to find your Contact ID.\n\nFor each recipient, you can optionally provide either:\n- `contact_id` to reference an existing contact\n- `contact` with `email` and `name` (and optionally `company` and `title`) to automatically create or update a contact\n\nBoth will set the recipient's contact automatically. You cannot provide both for the same recipient.\n\n**Template recipients and fields:** Parties (recipients) from the template are copied onto the new agreement, and each field keeps the same assignment as on the template (matched by contact). Request `recipients` can add more parties or update roles for contacts you include.\n\n**assigned_fields:** Optional per-recipient list of template field ids/names. When provided, only those fields are reassigned to that recipient; all other fields keep their template assignments (they do not fall back to the owner).\n\n**field_values:** Prefills values in the document without changing who each field is assigned to.\n","operationId":"AgreeWeb.API.V1.AgreementController.create","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgreementCreateParams"}}},"description":"Agreement create params","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgreementResponse"}}},"description":"Agreement created"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Template not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Create agreement from template","tags":["Agreements"]}},"/api/v1/reports/recovery/aging/chart":{"get":{"callbacks":{},"description":"Returns overdue invoice amounts by aging bucket for the past six months.","operationId":"AgreeWeb.API.V1.ReportController.recovery_aging_chart","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportListDataResponse"}}},"description":"Aging chart"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Aging chart data","tags":["Reports"]}},"/api/v1/webhooks/{id}":{"delete":{"callbacks":{},"description":"Deletes a webhook endpoint by ID.","operationId":"AgreeWeb.API.V1.WebhookEndpointController.delete","parameters":[{"description":"Webhook endpoint ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Webhook endpoint deleted"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Delete webhook endpoint","tags":["Webhooks"]},"get":{"callbacks":{},"description":"Returns a single webhook endpoint by ID.","operationId":"AgreeWeb.API.V1.WebhookEndpointController.show","parameters":[{"description":"Webhook endpoint ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointResponse"}}},"description":"Webhook endpoint"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Get webhook endpoint","tags":["Webhooks"]},"patch":{"callbacks":{},"description":"Updates an existing webhook endpoint.","operationId":"AgreeWeb.API.V1.WebhookEndpointController.update (2)","parameters":[{"description":"Webhook endpoint ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointParams"}}},"description":"Webhook endpoint params","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointResponse"}}},"description":"Webhook endpoint updated"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Update webhook endpoint","tags":["Webhooks"]},"put":{"callbacks":{},"description":"Updates an existing webhook endpoint.","operationId":"AgreeWeb.API.V1.WebhookEndpointController.update","parameters":[{"description":"Webhook endpoint ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointParams"}}},"description":"Webhook endpoint params","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointResponse"}}},"description":"Webhook endpoint updated"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Update webhook endpoint","tags":["Webhooks"]}},"/api/v1/agreements/templates/{id}":{"get":{"callbacks":{},"description":"Returns a single template by ID with field names.","operationId":"AgreeWeb.API.V1.AgreementController.show_template","parameters":[{"description":"Template ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateResponse"}}},"description":"Template"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Get template","tags":["Agreements"]}},"/api/v1/invoices/create_and_send":{"post":{"callbacks":{},"description":"Convenience endpoint that creates an invoice and sends it immediately.\n\nCombines create and send operations in a single request. The invoice will be scheduled for sending\nvia the invoice scheduler, which will send email notifications to the recipient.\n","operationId":"AgreeWeb.API.V1.InvoiceController.create_and_send","parameters":[],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceParams"}}},"description":"Invoice params","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}},"description":"Invoice created and sent"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Create and send invoice","tags":["Invoices"]}},"/api/v1/invoices/{id}/mark_as_paid":{"post":{"callbacks":{},"description":"Marks an invoice as paid with optional paid date and payment method.","operationId":"AgreeWeb.API.V1.InvoiceController.mark_as_paid","parameters":[{"description":"Invoice ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"paid_at":{"description":"Date when invoice was paid (ISO8601 format: YYYY-MM-DD). Defaults to today.","format":"date","type":"string"},"payment_method":{"description":"Payment method used (default: manual)","enum":["manual","ach","card","wire"],"type":"string"}},"type":"object"}}},"description":"Mark as paid params","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}},"description":"Invoice marked as paid"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Mark invoice as paid","tags":["Invoices"]}},"/api/v1/agreements/{id}":{"delete":{"callbacks":{},"description":"Deletes an agreement by ID (soft delete).","operationId":"AgreeWeb.API.V1.AgreementController.delete","parameters":[{"description":"Agreement ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Agreement deleted"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Delete agreement","tags":["Agreements"]},"get":{"callbacks":{},"description":"Returns a single agreement by ID.","operationId":"AgreeWeb.API.V1.AgreementController.show","parameters":[{"description":"Agreement ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgreementResponse"}}},"description":"Agreement"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"}},"security":[{"bearer":[]}],"summary":"Get agreement","tags":["Agreements"]},"patch":{"callbacks":{},"description":"Updates an existing agreement.\n\nThe following fields cannot be updated directly:\n- deleted_at\n- executed_at\n- organization_id\n- preview_url\n- status\n- version\n","operationId":"AgreeWeb.API.V1.AgreementController.update (2)","parameters":[{"description":"Agreement ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgreementParams"}}},"description":"Agreement params","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgreementResponse"}}},"description":"Agreement updated"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Update agreement","tags":["Agreements"]},"put":{"callbacks":{},"description":"Updates an existing agreement.\n\nThe following fields cannot be updated directly:\n- deleted_at\n- executed_at\n- organization_id\n- preview_url\n- status\n- version\n","operationId":"AgreeWeb.API.V1.AgreementController.update","parameters":[{"description":"Agreement ID (UUID)","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgreementParams"}}},"description":"Agreement params","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgreementResponse"}}},"description":"Agreement updated"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Forbidden"}}},"description":"Forbidden"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"Not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation errors"}},"security":[{"bearer":[]}],"summary":"Update agreement","tags":["Agreements"]}},"/api/v1/webhooks/test":{"post":{"callbacks":{},"description":"Sends a test webhook payload to all endpoints subscribed to the `webhook.test` event.\n\nThis is useful for verifying your webhook endpoint is properly configured to receive events.\n\nThe test payload contains:\n```json\n{\n  \"test\": true,\n  \"message\": \"This is a test webhook from Agree\",\n  \"timestamp\": \"2024-01-15T10:30:00Z\"\n}\n```\n","operationId":"AgreeWeb.API.V1.WebhookEndpointController.test","parameters":[],"responses":{"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestResponse"}}},"description":"Test webhooks queued"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFound"}}},"description":"No endpoints subscribed to webhook.test"}},"security":[{"bearer":[]}],"summary":"Send test webhook","tags":["Webhooks"]}},"/api/v1/reports/recovery/aging/invoices":{"get":{"callbacks":{},"description":"Lists outstanding invoices grouped by aging bucket (current, 1-30, 31-60, 61-90, 90+ days).","operationId":"AgreeWeb.API.V1.ReportController.recovery_aging_invoices","parameters":[],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportDataResponse"}}},"description":"Invoices by aging"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Unauthorized"}}},"description":"Unauthorized"}},"security":[{"bearer":[]}],"summary":"Invoices by aging bucket","tags":["Reports"]}}},"security":[],"servers":[{"url":"https://secure.agree.com","variables":{}}],"tags":[{"description":"Create, send, and manage agreements with recipients and field assignments.\n\n## Overview\n\nAgreements are documents that require signatures from one or more recipients. Each agreement is created from a template and can have specific fields (like signature fields, date fields, text fields) assigned to specific recipients.\n\n**Key concepts:**\n\n- Agreements are created from templates\n- Each agreement must have exactly one recipient with the `owner` role (the account holder)\n- Fields can be assigned to specific recipients via `assigned_fields`\n- Agreements can include invoices for payment collection\n- Recipients can be specified by `contact_id` or by providing contact details inline\n\n## Creating an Agreement\n\n### Basic Agreement Creation\n\nHere's a basic example of creating an agreement from a template:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/agreements \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"name\": \"Service Agreement\",\n    \"recipients\": [\n      {\n        \"contact_id\": \"770e8400-e29b-41d4-a716-446655440000\",\n        \"role\": \"owner\"\n      },\n      {\n        \"contact_id\": \"880e8400-e29b-41d4-a716-446655440000\",\n        \"role\": \"signer\"\n      }\n    ]\n  }'\n```\n\n### The Owner Role Requirement\n\n**Important:** When creating an agreement, exactly one recipient must be assigned the `owner` role. This recipient must be the account holder (the person whose API key is being used). The owner is the person initiating the agreement creation.\n\n**Common mistake:** If you assign yourself as a `signer` instead of `owner`, the request will fail with a validation error.\n\n**Correct approach:**\n```json\n{\n  \"recipients\": [\n    {\n      \"contact_id\": \"YOUR_CONTACT_ID\",\n      \"role\": \"owner\"\n    },\n    {\n      \"contact_id\": \"CLIENT_CONTACT_ID\",\n      \"role\": \"signer\"\n    }\n  ]\n}\n```\n\n**Incorrect approach (will fail):**\n```json\n{\n  \"recipients\": [\n    {\n      \"contact_id\": \"YOUR_CONTACT_ID\",\n      \"role\": \"signer\"  // ❌ Wrong - must be \"owner\"\n    }\n  ]\n}\n```\n\n### Finding Your Contact ID\n\nThe account holder (you) is also a contact in your organization. To find your own Contact ID:\n\n```bash\ncurl https://api.agree.com/api/v1/contacts \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\nThis returns a list of all contacts in your organization, including yourself. Look for the contact with your email address - that's your Contact ID. You can also filter by email:\n\n```bash\ncurl \"https://api.agree.com/api/v1/contacts?email=your-email@example.com\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Response:**\n```json\n{\n  \"data\": [\n    {\n      \"id\": \"770e8400-e29b-41d4-a716-446655440000\",\n      \"name\": \"Your Name\",\n      \"email\": \"your-email@example.com\",\n      \"company\": \"Your Company\",\n      ...\n    }\n  ],\n  \"pagination\": {\n    \"page\": 1,\n    \"page_size\": 10,\n    \"total_pages\": 1,\n    \"total_entries\": 1\n  }\n}\n```\n\nUse the `id` field from the contact that matches your email address as your `contact_id` when creating agreements.\n\n### Assigning Fields to Recipients\n\nWhen creating an agreement, you can assign specific fields to specific recipients. Fields are identified by their field IDs (as defined in the template).\n\n**Example: Assigning Fields**\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/agreements \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"name\": \"Service Agreement\",\n    \"recipients\": [\n      {\n        \"contact_id\": \"770e8400-e29b-41d4-a716-446655440000\",\n        \"role\": \"owner\",\n        \"assigned_fields\": [\"company_address\", \"date\"]\n      },\n      {\n        \"contact_id\": \"880e8400-e29b-41d4-a716-446655440000\",\n        \"role\": \"signer\",\n        \"assigned_fields\": [\"signature_field\", \"date_field\"]\n      }\n    ]\n  }'\n```\n\nIn this example:\n- The owner is assigned `company_address` and `date` fields\n- The signer is assigned `signature_field` and `date_field` fields\n\n**Field Assignment Rules:**\n\n- Field IDs must match exactly as defined in the template\n- Fields not assigned to any recipient will be assigned to the owner by default\n- You can assign multiple fields to the same recipient\n- The same field cannot be assigned to multiple recipients\n\n**Complete Example with All Options:**\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/agreements \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"name\": \"Service Agreement with Invoice\",\n    \"delivery_mode\": \"managed\",\n    \"field_values\": {\n      \"field_1\": \"John Doe\",\n      \"field_2\": \"2024-01-01\"\n    },\n    \"recipients\": [\n      {\n        \"contact_id\": \"770e8400-e29b-41d4-a716-446655440000\",\n        \"role\": \"owner\",\n        \"assigned_fields\": [\"company_address\", \"date\"]\n      },\n      {\n        \"contact\": {\n          \"email\": \"client@example.com\",\n          \"name\": \"Jane Smith\",\n          \"company\": \"Client Corp\"\n        },\n        \"role\": \"signer\",\n        \"assigned_fields\": [\"signature_field\", \"date_field\"]\n      }\n    ],\n    \"signing_order_enabled\": false,\n    \"payments_enabled\": true,\n    \"reminder_schedule\": \"weekly\",\n    \"invoice\": {\n      \"billing_contact\": {\n        \"email\": \"client@example.com\",\n        \"name\": \"Jane Smith\"\n      },\n      \"amount\": 15000,\n      \"currency\": \"USD\",\n      \"memo\": \"Payment for services\",\n      \"payment_methods\": [\"card\", \"ach\"],\n      \"payment_terms_type\": \"net\",\n      \"payment_terms_days\": 30\n    }\n  }'\n```\n\n## Daisy-Chaining: Attaching an Invoice to an Agreement\n\nThere are two ways to attach an invoice to an agreement:\n\n### Option 1: Create Agreement with Invoice (Single Request)\n\nThe simplest approach is to include the invoice in the agreement creation request:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/agreements \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"name\": \"Service Agreement with Invoice\",\n    \"recipients\": [\n      {\n        \"contact_id\": \"YOUR_CONTACT_ID\",\n        \"role\": \"owner\"\n      },\n      {\n        \"contact_id\": \"CLIENT_CONTACT_ID\",\n        \"role\": \"signer\"\n      }\n    ],\n    \"invoice\": {\n      \"billing_contact\": {\n        \"email\": \"client@example.com\",\n        \"name\": \"Jane Smith\"\n      },\n      \"amount\": 15000,\n      \"currency\": \"USD\",\n      \"memo\": \"Payment for services rendered\",\n      \"payment_methods\": [\"card\", \"ach\"],\n      \"payment_terms_type\": \"net\",\n      \"payment_terms_days\": 30\n    }\n  }'\n```\n\nThis creates both the agreement and an associated invoice template in a single API call. The invoice template is linked to the agreement via the `invoice_template_id` field.\n\n### Option 2: Two-Step Process (Create Agreement, Then Create Invoice)\n\nIf you need more control or want to create the invoice separately:\n\n**Step 1: Create the Agreement**\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/agreements \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"name\": \"Service Agreement\",\n    \"recipients\": [\n      {\n        \"contact_id\": \"YOUR_CONTACT_ID\",\n        \"role\": \"owner\"\n      },\n      {\n        \"contact_id\": \"CLIENT_CONTACT_ID\",\n        \"role\": \"signer\"\n      }\n    ]\n  }'\n```\n\n**Response includes agreement ID:**\n```json\n{\n  \"data\": {\n    \"id\": \"990e8400-e29b-41d4-a716-446655440000\",\n    ...\n  }\n}\n```\n\n**Step 2: Create Invoice and Link to Agreement**\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/invoices \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"invoice\": {\n      \"agreement_id\": \"990e8400-e29b-41d4-a716-446655440000\",\n      \"billing_contact\": {\n        \"email\": \"client@example.com\",\n        \"name\": \"Jane Smith\"\n      },\n      \"amount\": 15000,\n      \"currency\": \"USD\",\n      \"memo\": \"Payment for services rendered\",\n      \"payment_methods\": [\"card\", \"ach\"],\n      \"payment_terms_type\": \"net\",\n      \"payment_terms_days\": 30\n    }\n  }'\n```\n\n**When to use each approach:**\n\n- **Option 1 (single request):** Use when you want to create the agreement and invoice together atomically. This is simpler and ensures the invoice is always linked to the agreement.\n\n- **Option 2 (two-step):** Use when you need to:\n  - Create the agreement first and review it before adding the invoice\n  - Create multiple invoices for the same agreement\n  - Have more control over the invoice creation timing\n  - Handle errors separately for agreement vs invoice creation\n\n## Recipient Roles\n\n| Role | Description |\n|------|-------------|\n| `owner` | The account holder initiating the agreement. Exactly one recipient must have this role. |\n| `signer` | A recipient who needs to sign the agreement |\n| `viewer` | A recipient who can view but not sign the agreement |\n| `payee` | A recipient who will receive payment (used with invoices) |\n\n## Field Values (Prefilling)\n\nYou can prefill field values when creating an agreement:\n\n```json\n{\n  \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"name\": \"Service Agreement\",\n  \"field_values\": {\n    \"field_1\": \"John Doe\",\n    \"field_2\": \"2024-01-01\",\n    \"company_name\": \"Acme Corp\"\n  },\n  \"recipients\": [...]\n}\n```\n\nKeys in `field_values` must match **`field_id`** values from your template. Fetch the template to list `field_names` (non-variable fields) and `variables` (each variable’s `field_id` and display `name`):\n\n```bash\ncurl https://api.agree.com/api/v1/agreements/templates/550e8400-e29b-41d4-a716-446655440000 \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\nThe response includes a `field_names` array listing all available fields in the template, plus a `variables` array for template variables (see below). Use each field’s **`field_id`** as the key in `field_values` (for variables, use the `field_id` from the `variables` entry, not the display `name`).\n\n### Typed values and rich text\n\nEach entry in `field_values` can be either:\n\n1. **A string** (legacy): plain text. Works for any field or variable.\n2. **An object** with a `content` string and optional `content_type`:\n   ```json\n   {\n     \"content\": \"Renewal 2026-05-01\",\n     \"content_type\": \"html\"\n   }\n   ```\n   - `content` (required): the payload.\n   - `content_type` (optional): `plaintext`, `html`, or `markdown`. If omitted, **`plaintext`** is used.\n\n**Rich text applies only to template variables.** If the `field_values` key matches a **variable** `field_id` (from `GET /api/v1/agreements/templates/:id` → `data.variables`), then `content_type` **`html`** or **`markdown`** is converted into styled inline content in the agreement body (bold, italics, line breaks, HTML lists, markdown list lines with `-` / `*`, etc.).\n\nFor **all non-variable fields** (text, date, signature, checkbox, and every other fillable field), typed objects are accepted, but **only the plain-text form** is stored on the field—**formatting is not preserved**. Use plain strings for those unless you only need a simple string payload.\n\nInvalid typed objects (for example missing `content` or an invalid `content_type`) return **400 Bad Request** with an error referencing `field_values`.\n\n## Custom Variables (Template Variables)\n\nTemplates can contain **custom variables** — placeholder fields for dynamic content like names, dates, or amounts. Variables remain as live fields in the agreement until it is sent, at which point they are resolved into plain text.\n\nVariable values can be provided at creation time via `field_values`, or filled in later through the editor UI. **All variables must have values before the agreement can be sent.**\n\n**Rich text:** Only keys that correspond to **variables** (see `variables[].field_id`) honor `content_type` of `html` or `markdown` and keep formatting in the document. Other fields always receive plain text only—see [Typed values and rich text](#typed-values-and-rich-text).\n\n### Step 1: Discover Template Variables\n\nFetch the template to see its variables:\n\n```bash\ncurl https://api.agree.com/api/v1/agreements/templates/TEMPLATE_ID \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Response:**\n```json\n{\n  \"data\": {\n    \"id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"name\": \"Employment Agreement\",\n    \"field_names\": [\"signature_field\", \"date_field\"],\n    \"variables\": [\n      {\n        \"name\": \"Employee Name\",\n        \"field_id\": \"var_abc123\"\n      },\n      {\n        \"name\": \"Start Date\",\n        \"field_id\": \"var_def456\"\n      },\n      {\n        \"name\": \"Salary\",\n        \"field_id\": \"var_ghi789\"\n      }\n    ]\n  }\n}\n```\n\nThe `variables` array lists each custom variable with its `name` (display label) and `field_id` (the key to use in `field_values`).\n\n### Step 2: Provide Variable Values (Optional at Creation)\n\nWhen creating the agreement, you can pre-fill variable values via `field_values`. Any variables not provided will remain as unfilled live fields in the agreement.\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/agreements \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"name\": \"Employment Agreement - Jane Smith\",\n    \"field_values\": {\n      \"var_abc123\": \"Jane Smith\",\n      \"var_def456\": \"2025-03-01\",\n      \"var_ghi789\": \"$120,000\"\n    },\n    \"recipients\": [\n      {\n        \"contact_id\": \"YOUR_CONTACT_ID\",\n        \"role\": \"owner\"\n      },\n      {\n        \"contact\": {\n          \"email\": \"jane@example.com\",\n          \"name\": \"Jane Smith\"\n        },\n        \"role\": \"signer\",\n        \"assigned_fields\": [\"signature_field\", \"date_field\"]\n      }\n    ]\n  }'\n```\n\nWhen the agreement is sent, variable values are resolved into plain text — recipients will see \"Jane Smith\" rather than a placeholder (rich variable content is flattened at send time).\n\n### Rich text examples (variables only)\n\nPlain string (unchanged):\n\n```json\n\"var_abc123\": \"Jane Smith\"\n```\n\nHTML (lists, emphasis, etc.):\n\n```json\n\"var_schedule\": {\n  \"content_type\": \"html\",\n  \"content\": \"Payment 1 on 2026-03-17Payment 2 on 2026-04-17\"\n}\n```\n\nMarkdown (line breaks, `-` / `*` list lines, `**bold**`, `*italic*`, `_italic_`):\n\n```json\n\"var_schedule\": {\n  \"content_type\": \"markdown\",\n  \"content\": \"- **First** payment on 2026-03-17\\n- Second payment on 2026-04-17\"\n}\n```\n\nDefault to plaintext when `content_type` is omitted:\n\n```json\n\"var_note\": { \"content\": \"Shown as plain text only\" }\n```\n\n### Error Handling\n\nIf you attempt to send an agreement with unfilled variables, the API returns a `400 Bad Request`:\n\n```json\n{\n  \"error\": \"Unfilled variables: Employee Name, Start Date. All variables must have values before sending.\"\n}\n```\n\n## Listing Agreements\n\nRetrieve agreements with optional filtering:\n\n```bash\n# Get all agreements\ncurl https://api.agree.com/api/v1/agreements \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n\n# Filter by status\ncurl \"https://api.agree.com/api/v1/agreements?status=drafted\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n### Query Parameters\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `page` | integer | Page number (default: 1) |\n| `page_size` | integer | Items per page (default: 10, max: 100) |\n| `status` | string | Filter by status: `created`, `drafted`, `sent`, `signed`, `executed`, `terminated` |\n\n## Sending an Agreement\n\nAfter creating an agreement, send it to recipients:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/agreements/990e8400-e29b-41d4-a716-446655440000/send \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"delivery_mode\": \"managed\"\n  }'\n```\n\n**Delivery Modes:**\n\n- `managed` - Agree sends emails to recipients automatically\n- `embedded` - Emails are suppressed (you handle delivery yourself)\n\n## Create and Send in One Step\n\nFor convenience, create and send an agreement in a single request:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/agreements/create_and_send \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"name\": \"Service Agreement\",\n    \"delivery_mode\": \"managed\",\n    \"recipients\": [\n      {\n        \"contact_id\": \"YOUR_CONTACT_ID\",\n        \"role\": \"owner\"\n      },\n      {\n        \"contact_id\": \"CLIENT_CONTACT_ID\",\n        \"role\": \"signer\"\n      }\n    ]\n  }'\n```\n\n## Updating an Agreement\n\nUpdate agreement details and recipients. You can include `field_values` inside `agreement` the same way as on create; **rich text (`html` / `markdown`) still applies only to template variables**—see [Typed values and rich text](#typed-values-and-rich-text).\n\n```bash\ncurl -X PUT https://api.agree.com/api/v1/agreements/990e8400-e29b-41d4-a716-446655440000 \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"agreement\": {\n      \"name\": \"Updated Service Agreement\",\n      \"recipients\": [\n        {\n          \"contact_id\": \"YOUR_CONTACT_ID\",\n          \"role\": \"owner\",\n          \"assigned_fields\": [\"company_address\"]\n        },\n        {\n          \"contact_id\": \"CLIENT_CONTACT_ID\",\n          \"role\": \"signer\",\n          \"assigned_fields\": [\"signature_field\"]\n        }\n      ]\n    }\n  }'\n```\n\n**Note:** Updating recipients replaces all existing recipients. Make sure to include all recipients you want to keep.\n\n## Agreement Statuses\n\n| Status | Description |\n|--------|-------------|\n| `created` | Agreement created but not yet finalized |\n| `drafted` | Agreement is in draft state (default when created) |\n| `sent` | Agreement has been sent to recipients |\n| `viewed` | At least one recipient has viewed the agreement |\n| `signed` | At least one recipient has signed |\n| `executed` | Agreement is fully executed (all required signatures collected) |\n| `renewed` | Agreement has been renewed |\n| `terminated` | Agreement has been terminated |\n\n## Fields Reference\n\n### Core Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `id` | UUID | Unique agreement identifier |\n| `name` | string | Agreement name |\n| `status` | string | Current status (see statuses above) |\n| `template_id` | UUID | Template used to create this agreement |\n| `organization_id` | UUID | Your organization's ID |\n| `invoice_template_id` | UUID | Associated invoice template (if invoice was created) |\n\n### Recipient Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `recipients` | array | List of recipients with their roles and assigned fields |\n| `signing_order` | array | List of recipient IDs in signing order (if enabled) |\n| `signing_order_enabled` | boolean | Whether signing order is enforced |\n\n### Delivery Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `delivery_mode` | string | `embedded` or `managed` |\n| `reminder_schedule` | string | `none`, `daily`, `weekly`, or `monthly` |\n| `reminder_scheduled_at` | datetime | When the next reminder will be sent |\n\n### Date Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `starts_at` | datetime | When the agreement starts |\n| `ends_at` | datetime | When the agreement ends |\n| `executed_at` | datetime | When the agreement was fully executed |\n| `last_reminder_sent_at` | datetime | When the last reminder was sent |\n","name":"Agreements"},{"description":"Create, send, and track payment requests to your customers.\n\n## Overview\n\nInvoices are the core of Agree's payment system. An invoice represents a request for payment that you send to a customer. When created, Agree generates a secure payment link that your customer can use to pay via their preferred method.\n\n**Key concepts:**\n\n- Invoices are sent to contacts (customers in your address book)\n- Each invoice supports multiple payment methods: ACH bank transfer, credit card, or wire transfer\n- Invoices can be one-time or recurring on a schedule\n- Automatic email delivery sends the payment link to your customer\n- Webhooks notify you in real-time when payment status changes\n\n## Common Use Cases\n\n### Bill a Client for a Completed Project\n\nSend a one-time invoice after completing work:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/invoices \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"invoice\": {\n      \"billing_contact\": {\n        \"email\": \"client@company.com\",\n        \"name\": \"Sarah Johnson\",\n        \"company\": \"Johnson & Co\"\n      },\n      \"amount\": {\"amount\": 500000, \"currency\": \"USD\"},\n      \"payment_methods\": [\"card\", \"ach\", \"wire\"],\n      \"due_at\": \"2025-02-01T00:00:00Z\",\n      \"memo\": \"Website redesign project - Final payment\"\n    }\n  }'\n```\n\nThe client receives an email with a payment link. You'll get a webhook when they pay.\n\n### Set Up Monthly Retainer Billing\n\nCreate a recurring invoice that bills automatically each month:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/invoices \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"invoice\": {\n      \"billing_contact\": {\n        \"email\": \"accounting@bigcorp.com\",\n        \"name\": \"Accounts Payable\",\n        \"company\": \"BigCorp Inc\"\n      },\n      \"amount\": {\"amount\": 250000, \"currency\": \"USD\"},\n      \"payment_methods\": [\"ach\"],\n      \"memo\": \"Monthly consulting retainer\",\n      \"recurring_options\": {\n        \"schedule\": \"custom\",\n        \"repeat_frequency\": 1,\n        \"repeat_unit\": \"month\",\n        \"repeat_on_type\": \"day_of_month\",\n        \"repeat_on_day\": 1,\n        \"recurring_end_type\": \"never\",\n        \"reminder_schedule\": \"weekly\"\n      }\n    }\n  }'\n```\n\nAgree automatically generates and sends invoices on the 1st of each month.\n\n### Track Outstanding Invoices\n\nFind all unpaid invoices that are past due:\n\n```bash\ncurl \"https://api.agree.com/api/v1/invoices?statuses=sent,due&date_type=due_at&date_end=2025-01-17\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n### Handle Failed Payments\n\nWhen a payment fails, you receive an `invoice.failed` webhook. The invoice status changes to `failed`, but the customer can retry payment using the same link. To check failed invoices:\n\n```bash\ncurl \"https://api.agree.com/api/v1/invoices?statuses=failed\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n### Generate a Revenue Report\n\nGet all paid invoices for a specific month:\n\n```bash\ncurl \"https://api.agree.com/api/v1/invoices?statuses=paid&date_type=paid_at&date_start=2025-01-01&date_end=2025-01-31\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n## Creating an Invoice\n\nHere's a basic invoice creation:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/invoices \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"invoice\": {\n      \"billing_contact\": {\n        \"email\": \"customer@example.com\",\n        \"name\": \"John Doe\",\n        \"company\": \"Acme Corp\"\n      },\n      \"amount\": {\"amount\": 15000, \"currency\": \"USD\"},\n      \"payment_methods\": [\"card\", \"ach\"],\n      \"due_at\": \"2025-02-15T00:00:00Z\",\n      \"scheduled_at\": \"2025-02-01T00:00:00Z\",\n      \"memo\": \"Website development - Phase 1\"\n    }\n  }'\n```\n\n**Note:** When using the `create` endpoint, `due_at` and `scheduled_at` are required fields. The invoice issue date (`inserted_at`) is automatically set when the invoice is created. All dates should be in ISO8601 format (UTC).\n\n**Response:**\n\n```json\n{\n  \"data\": {\n    \"id\": \"4a755746-ba45-4226-a669-aebc7ad3719c\",\n    \"status\": \"sent\",\n    \"amount\": {\"amount\": 15000, \"currency\": \"USD\"},\n    \"billing_contact\": {\n      \"email\": \"customer@example.com\",\n      \"name\": \"John Doe\",\n      \"company\": \"Acme Corp\",\n      \"title\": null\n    },\n    \"payment_link\": \"https://agree.com/pay/abc123token\",\n    \"payment_methods\": [\"card\", \"ach\"],\n    \"due_at\": \"2025-02-15T00:00:00Z\",\n    \"scheduled_at\": \"2025-02-01T00:00:00Z\",\n    \"memo\": \"Website development - Phase 1\",\n    \"inserted_at\": \"2025-01-15T10:30:00Z\"\n  }\n}\n```\n\nThe `payment_link` is a secure URL you can share with your customer. When `automatic_delivery` is enabled (the default), Agree emails this link to the billing contact automatically.\n\n### Create and Send in One Step\n\nFor convenience, you can create and send an invoice in a single API call:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/invoices/create_and_send \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"invoice\": {\n      \"billing_contact\": {\n        \"email\": \"customer@example.com\",\n        \"name\": \"John Doe\"\n      },\n      \"amount\": {\"amount\": 15000, \"currency\": \"USD\"},\n      \"payment_methods\": [\"card\", \"ach\"],\n      \"memo\": \"Website development - Phase 1\"\n    }\n  }'\n```\n\n**Note:** When using `create_and_send`, `scheduled_at` is always set to the current UTC time (to send immediately), regardless of any value you provide. If `due_at` is not provided, it will default to the current UTC time. The invoice issue date (`inserted_at`) is automatically set when the invoice is created. You can optionally specify a custom `due_at`:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/invoices/create_and_send \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"invoice\": {\n      \"billing_contact\": {\n        \"email\": \"customer@example.com\",\n        \"name\": \"John Doe\"\n      },\n      \"amount\": {\"amount\": 15000, \"currency\": \"USD\"},\n      \"payment_methods\": [\"card\", \"ach\"],\n      \"due_at\": \"2025-02-15T00:00:00Z\",\n      \"memo\": \"Website development - Phase 1\"\n    }\n  }'\n```\n\nNote that `scheduled_at` is always set to the current UTC time when using `create_and_send`, so it's not necessary (and will be ignored) if provided.\n\n**Response:**\n\n```json\n{\n  \"data\": {\n    \"id\": \"4a755746-ba45-4226-a669-aebc7ad3719c\",\n    \"status\": \"sending\",\n    \"amount\": {\"amount\": 15000, \"currency\": \"USD\"},\n    ...\n  }\n}\n```\n\nThe response includes `status: \"sending\"` to indicate the invoice is being sent asynchronously. The invoice will transition to `sent` once the email is delivered.\n\n**When to use `create_and_send`:**\n- You want to create and send an invoice immediately in one request\n- You don't need to review or modify the invoice before sending\n- You want to simplify your integration by combining two operations\n\n**When to use `create` + `send` separately:**\n- You need to review the invoice before sending\n- You want to add additional information after creation\n- You're creating invoices in bulk and want to send them later\n\n### Amounts\n\nAmounts are specified in the smallest currency unit. For USD, this means cents:\n\n| You want to charge | Send this amount |\n|--------------------|------------------|\n| $100.00 | `10000` |\n| $1,500.50 | `150050` |\n| $0.99 | `99` |\n\n```json\n{\n  \"amount\": {\n    \"amount\": 10000,\n    \"currency\": \"USD\"\n  }\n}\n```\n\n### Specifying the Customer\n\nYou can specify who receives the invoice in two ways:\n\n**Using `billing_contact`** (recommended for new customers):\n```json\n{\n  \"invoice\": {\n    \"billing_contact\": {\n      \"email\": \"customer@example.com\",\n      \"name\": \"John Doe\"\n    }\n  }\n}\n```\nThis creates or updates a contact automatically.\n\n**Using `contact_id`** (for existing contacts):\n```json\n{\n  \"invoice\": {\n    \"contact_id\": \"550e8400-e29b-41d4-a716-446655440000\"\n  }\n}\n```\n\nYou cannot use both in the same request.\n\n## Invoice Lifecycle\n\nEvery invoice progresses through a series of statuses:\n\n```\n┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐\n│ created │ ──► │  sent   │ ──► │   due   │ ──► │  paid   │\n└─────────┘     └─────────┘     └─────────┘     └─────────┘\n                                     │\n                                     ├──► processing ──► paid\n                                     │                    │\n                                     │                    └──► failed\n                                     │\n                                     └──► canceled\n```\n\n| Status | Description |\n|--------|-------------|\n| `created` | Invoice created but not yet sent to customer |\n| `sending` | Invoice is being sent (temporary status returned by API) |\n| `sent` | Invoice emailed to customer, awaiting payment |\n| `due` | Invoice is past the scheduled send date |\n| `processing` | Payment initiated, waiting for confirmation |\n| `paid` | Payment completed successfully |\n| `failed` | Payment attempt failed (customer can retry) |\n| `canceled` | Invoice was canceled (no payment expected) |\n| `refunded` | Payment was refunded after completion |\n| `draft` | Template-only, not yet converted to invoice |\n\n**Note:** The `sending` status is a temporary status returned by the API when you use `create_and_send` or `send` endpoints. It indicates the invoice is being sent asynchronously. When you query the invoice later, it will show `sent` (or `due` if sent immediately with a past due date).\n\n## Recurring Invoices\n\nSet up automatic recurring invoices by providing `recurring_options`:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/invoices \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"invoice\": {\n      \"billing_contact\": {\"email\": \"customer@example.com\"},\n      \"amount\": {\"amount\": 99900, \"currency\": \"USD\"},\n      \"payment_methods\": [\"card\"],\n      \"recurring_options\": {\n        \"schedule\": \"custom\",\n        \"repeat_frequency\": 1,\n        \"repeat_unit\": \"month\",\n        \"recurring_end_type\": \"never\",\n        \"reminder_schedule\": \"weekly\"\n      }\n    }\n  }'\n```\n\n### Recurring Options\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `schedule` | string | `none` (one-time) or `custom` (recurring) |\n| `repeat_frequency` | integer | How often to repeat (e.g., `1` = every period, `2` = every other) |\n| `repeat_unit` | string | `week` or `month` |\n| `repeat_on_weekday` | string | For weekly: `monday`, `tuesday`, etc. |\n| `repeat_on_type` | string | For monthly: `day_of_month` or `day_of_week` |\n| `repeat_on_day` | integer | Day of month (1-31) |\n| `repeat_on_week` | integer | Week of month (1-5, where 5 = last) |\n| `recurring_end_type` | string | `never`, `date`, or `count` |\n| `recurring_end_date` | datetime | End date (when type is `date`) |\n| `recurring_end_count` | integer | Number of occurrences (when type is `count`) |\n| `reminder_schedule` | string | `none`, `daily`, `weekly`, or `monthly` |\n| `forward_payment_enabled` | boolean | Allow paying future invoices early |\n| `pass_on_fees_enabled` | boolean | Pass processing fees to the payer at checkout (default: false) |\n\n### Examples\n\n**Monthly on the 15th, forever:**\n```json\n{\n  \"schedule\": \"custom\",\n  \"repeat_frequency\": 1,\n  \"repeat_unit\": \"month\",\n  \"repeat_on_type\": \"day_of_month\",\n  \"repeat_on_day\": 15,\n  \"recurring_end_type\": \"never\"\n}\n```\n\n**Every 2 weeks on Monday, for 6 occurrences:**\n```json\n{\n  \"schedule\": \"custom\",\n  \"repeat_frequency\": 2,\n  \"repeat_unit\": \"week\",\n  \"repeat_on_weekday\": \"monday\",\n  \"recurring_end_type\": \"count\",\n  \"recurring_end_count\": 6\n}\n```\n\n## Listing and Filtering Invoices\n\nRetrieve invoices with powerful filtering options:\n\n```bash\n# Get all invoices\ncurl https://api.agree.com/api/v1/invoices \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n\n# Filter by status\ncurl \"https://api.agree.com/api/v1/invoices?statuses=sent,due\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n\n# Filter by date range (invoices due in January 2025)\ncurl \"https://api.agree.com/api/v1/invoices?date_type=due_at&date_start=2025-01-01&date_end=2025-01-31\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n\n# Filter by amount range ($100-$500)\ncurl \"https://api.agree.com/api/v1/invoices?amount_min=100&amount_max=500\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n### Query Parameters\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `page` | integer | Page number (default: 1) |\n| `page_size` | integer | Items per page (default: 10, max: 100) |\n| `statuses` | string | Comma-separated status filter |\n| `date_start` | string | Start date (YYYY-MM-DD) |\n| `date_end` | string | End date (YYYY-MM-DD) |\n| `date_type` | string | Which date to filter: `paid_at`, `due_at`, `scheduled_at` |\n| `date_timezone` | string | Timezone for dates (default: `Etc/UTC`) |\n| `amount_min` | number | Minimum amount in dollars |\n| `amount_max` | number | Maximum amount in dollars |\n| `customer` | string | Filter by customer/company name |\n| `include_drafts` | boolean | Include draft invoices |\n\n## Sending an Invoice\n\nIf you created an invoice without sending it (or want to resend), you can send it explicitly:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/invoices/4a755746-ba45-4226-a669-aebc7ad3719c/send \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Response:**\n\n```json\n{\n  \"data\": {\n    \"id\": \"4a755746-ba45-4226-a669-aebc7ad3719c\",\n    \"status\": \"sending\",\n    ...\n  }\n}\n```\n\nThe response includes `status: \"sending\"` to indicate the invoice is being sent asynchronously. The invoice will transition to `sent` once the email is delivered.\n\n**Note:** You can only send invoices that are in `created` status. Invoices that are already `sent`, `due`, or `paid` cannot be resent using this endpoint.\n\n## Updating an Invoice\n\nUpdate invoice details before payment:\n\n```bash\ncurl -X PUT https://api.agree.com/api/v1/invoices/4a755746-ba45-4226-a669-aebc7ad3719c \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"invoice\": {\n      \"memo\": \"Updated memo - Website development Phase 1\",\n      \"due_at\": \"2025-02-28T00:00:00Z\"\n    }\n  }'\n```\n\n**Note:** Some fields cannot be changed after certain status transitions (e.g., you can't change the amount after payment processing begins).\n\n## Downloading invoice and receipt PDFs\n\nThese endpoints return a **presigned S3 URL** in JSON (not the raw PDF bytes). The URL is valid for **one hour** (`expires_in: 3600`). Use a GET to the returned `url` to download the file (e.g. redirect the user or fetch server-side).\n\n### Invoice PDF\n\n```bash\ncurl \"https://api.agree.com/api/v1/invoices/INVOICE_ID/pdf\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Response (200):**\n\n```json\n{\n  \"data\": {\n    \"url\": \"https://...\",\n    \"expires_in\": 3600\n  }\n}\n```\n\nIf the PDF is not in storage yet, the API waits a **short time** (default **5 seconds** of server-side polling, configurable via `invoice_pdf_api_inline_wait_ms`) for the file to appear—first in case another client already started generation, then after enqueueing generation if needed. If it is still not ready, you receive **202 Accepted** with a **`Retry-After`** header (default **3** seconds, `invoice_pdf_api_retry_after_seconds`) and a JSON body such as `data: { \"status\": \"pending\", \"retry_after_seconds\": 3, ... }`. **Repeat the same GET** until you get **200** with `data.url`. This avoids holding many long-lived HTTP connections when PDFs are slow or the render queue is busy.\n\n### Receipt PDF\n\nOnly available when the invoice status is **`paid`**. Otherwise the API returns **422**.\n\n```bash\ncurl \"https://api.agree.com/api/v1/invoices/INVOICE_ID/receipt_pdf\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Response (200):** Same shape as invoice PDF (`data.url`, `data.expires_in`).\n\nIf the invoice is paid but the receipt file is not available yet, the API returns **404**.\n\n## Canceling an Invoice\n\nCancel an unpaid invoice:\n\n```bash\ncurl -X DELETE https://api.agree.com/api/v1/invoices/4a755746-ba45-4226-a669-aebc7ad3719c \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n## Fields Reference\n\n### Core Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `id` | UUID | Unique invoice identifier |\n| `name` | string | Invoice display name |\n| `status` | string | Current status (see lifecycle) |\n| `amount` | object | Amount with `amount` (cents) and `currency` |\n| `memo` | string | Notes visible to customer (max 255 chars) |\n| `organization_id` | UUID | Your organization's ID |\n| `agreement_id` | UUID | Associated agreement (if any) |\n\n### Customer Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `billing_contact` | object | Customer info: `email`, `name`, `company`, `title` |\n| `payment_link` | string | URL where customer can pay |\n\n### Payment Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `payment_methods` | array | Accepted methods: `ach`, `card`, `wire` |\n| `payment_type` | string | `invoice`, `payment`, or `subscription` |\n| `used_payment_method` | string | Method used for successful payment |\n| `sales_tax_percentage` | number | Tax percentage applied |\n\n### Date Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `scheduled_at` | datetime | When invoice will be/was sent |\n| `sent_at` | datetime | When invoice was emailed |\n| `due_at` | datetime | Payment due date |\n| `paid_at` | datetime | When payment completed |\n| `processing_at` | datetime | When processing started |\n| `authorized_at` | datetime | When payment was authorized |\n| `inserted_at` | datetime | When invoice was created |\n\n### Delivery Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `delivery_method` | string | How invoice is delivered (`email`) |\n| `automatic_delivery` | boolean | Auto-send when created |\n\n### Recurring Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `recurring_options` | object | Recurring schedule configuration |\n| `recurring_sequence` | integer | Position in recurring series (1, 2, 3...) |\n\n### Reminder Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `reminder_scheduled_at` | datetime | Next reminder date |\n| `last_reminder_sent_at` | datetime | Last reminder sent |\n\n### External Reference Fields\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `external_id` | string | Your external invoice ID |\n| `external_customer_id` | string | Your external customer ID |\n| `destination_organization_id` | UUID | For B2B: receiving organization |\n","name":"Invoices"},{"description":"Manage your organization's contacts - the people and companies you do business with.\n\n## Overview\n\nContacts are the foundation of your billing workflow. Before you can send an invoice, you need someone to send it to. Contacts store customer information like name, email, company, and job title.\n\n**Key concepts:**\n\n- Each contact belongs to a single organization\n- Email addresses must be unique within your organization\n- Contacts can be created explicitly via the API, or automatically when you send an invoice to a new email address\n- Deleting a contact is a soft delete - the record is retained for historical invoices\n\n## Creating a Contact\n\nTo add a new contact to your address book:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/contacts \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"contact\": {\n      \"name\": \"Jane Smith\",\n      \"email\": \"jane@acme.com\",\n      \"company\": \"Acme Corporation\",\n      \"title\": \"CFO\"\n    }\n  }'\n```\n\n**Response:**\n\n```json\n{\n  \"data\": {\n    \"id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"name\": \"Jane Smith\",\n    \"email\": \"jane@acme.com\",\n    \"company\": \"Acme Corporation\",\n    \"title\": \"CFO\",\n    \"address\": null,\n    \"organization_id\": \"660e8400-e29b-41d4-a716-446655440000\",\n    \"inserted_at\": \"2025-01-15T10:30:00Z\",\n    \"updated_at\": \"2025-01-15T10:30:00Z\"\n  }\n}\n```\n\n## Using Contacts with Invoices\n\nOnce you have a contact, you can reference them when creating invoices. There are two ways to associate a contact with an invoice:\n\n### Option 1: Use `contact_id`\n\nIf you already have a contact, pass their ID:\n\n```json\n{\n  \"invoice\": {\n    \"contact_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"amount\": {\"amount\": 10000, \"currency\": \"USD\"}\n  }\n}\n```\n\n### Option 2: Use `billing_contact`\n\nPass contact details directly - this will find or create the contact automatically:\n\n```json\n{\n  \"invoice\": {\n    \"billing_contact\": {\n      \"email\": \"jane@acme.com\",\n      \"name\": \"Jane Smith\",\n      \"company\": \"Acme Corporation\"\n    },\n    \"amount\": {\"amount\": 10000, \"currency\": \"USD\"}\n  }\n}\n```\n\nIf a contact with that email already exists, their details will be updated. If not, a new contact is created.\n\n## Listing and Filtering Contacts\n\nRetrieve contacts with optional filtering:\n\n```bash\n# Get all contacts\ncurl https://api.agree.com/api/v1/contacts \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n\n# Search by email\ncurl \"https://api.agree.com/api/v1/contacts?email=jane\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n\n# Filter by company\ncurl \"https://api.agree.com/api/v1/contacts?company=acme\" \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n### Query Parameters\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `page` | integer | Page number (default: 1) |\n| `page_size` | integer | Items per page (default: 10, max: 100) |\n| `email` | string | Filter by email address (fuzzy search) |\n| `company` | string | Filter by company name (fuzzy search) |\n\n## Updating a Contact\n\nUpdate contact details using PUT:\n\n```bash\ncurl -X PUT https://api.agree.com/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000 \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"contact\": {\n      \"title\": \"CEO\",\n      \"company\": \"Acme Corp International\"\n    }\n  }'\n```\n\n## Deleting a Contact\n\nDelete a contact by ID:\n\n```bash\ncurl -X DELETE https://api.agree.com/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000 \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n**Note:** This performs a soft delete. The contact record is retained for historical purposes (existing invoices will still show the contact information), but will no longer appear in your contacts list.\n\n## Fields Reference\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `id` | UUID | Unique contact identifier |\n| `name` | string | Contact's full name (required) |\n| `email` | string | Contact's email address (required, unique per organization) |\n| `company` | string | Company or organization name |\n| `title` | string | Job title or role |\n| `address` | string | Mailing address |\n| `organization_id` | UUID | Your organization's ID |\n| `inserted_at` | datetime | When the contact was created |\n| `updated_at` | datetime | When the contact was last updated |\n","name":"Contacts"},{"description":"Receive real-time notifications when events occur in your Agree account.\n\n## Overview\n\nWebhooks push event data to your application as soon as something happens - like when an invoice is paid or a payment fails. This eliminates the need to poll the API for updates and lets you respond to events instantly.\n\n**Key concepts:**\n\n- You register a URL endpoint that Agree will call when events occur\n- Each endpoint can subscribe to specific event types\n- Webhook payloads are signed so you can verify they came from Agree\n- Failed deliveries are automatically retried with exponential backoff\n\n## Quick Setup\n\n### 1. Create an Endpoint\n\nRegister a URL to receive webhooks:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/webhook_endpoints \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"webhook_endpoint\": {\n      \"url\": \"https://your-app.com/webhooks/agree\",\n      \"events\": [\"invoice.paid\", \"invoice.failed\"]\n    }\n  }'\n```\n\n**Response:**\n\n```json\n{\n  \"data\": {\n    \"id\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"url\": \"https://your-app.com/webhooks/agree\",\n    \"events\": [\"invoice.paid\", \"invoice.failed\"],\n    \"active\": true,\n    \"failure_count\": 0,\n    \"secret\": \"whsec_abc123xyz789...\",\n    \"inserted_at\": \"2025-01-15T10:30:00Z\",\n    \"updated_at\": \"2025-01-15T10:30:00Z\"\n  }\n}\n```\n\n**Important:** Save the `secret` - it's only returned once at creation. You'll need it to verify webhook signatures.\n\n### 2. Handle Incoming Webhooks\n\nWhen an event occurs, Agree sends a POST request to your endpoint:\n\n```json\n{\n  \"event\": \"invoice.paid\",\n  \"payload\": {\n    \"id\": \"4a755746-ba45-4226-a669-aebc7ad3719c\",\n    \"status\": \"paid\",\n    \"amount\": {\"amount\": 15000, \"currency\": \"USD\"},\n    \"paid_at\": \"2025-01-20T14:30:00Z\"\n  }\n}\n```\n\n### 3. Verify the Signature\n\nAlways verify webhooks came from Agree before processing them. See [Verifying Webhooks](#verifying-webhooks) below.\n\n### 4. Test Your Integration\n\nSend a test webhook to verify your endpoint works:\n\n```bash\ncurl -X POST https://api.agree.com/api/v1/webhook_endpoints/550e8400-e29b-41d4-a716-446655440000/test \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n## Available Events\n\nSubscribe to the events your application needs:\n\n| Event | Description | When it fires |\n|-------|-------------|---------------|\n| `invoice.created` | Invoice was created | After POST /invoices |\n| `invoice.sent` | Invoice emailed to customer | When delivery completes |\n| `invoice.due` | Invoice reached due status | When status becomes `due`: either the scheduled job runs after `sent` (past `due_at`), or the invoice is sent already at/past `due_at` (immediate `due`) |\n| `invoice.paid` | Payment successful | After payment confirmation |\n| `invoice.failed` | Payment attempt failed | After payment rejection |\n| `invoice.canceled` | Invoice was canceled | After DELETE /invoices |\n| `invoice.refunded` | Invoice payment was refunded | After refund is processed |\n| `agreement.created` | Agreement was created | After POST /agreements |\n| `agreement.sent` | Agreement was sent to recipients | When status changes to 'sent' |\n| `agreement.signed` | Agreement was signed by a recipient | When a recipient signs |\n| `agreement.executed` | Agreement was fully executed | When all signers have signed |\n| `webhook.test` | Test event | When you trigger a test |\n\n**Tip:** Start with `invoice.paid` and `invoice.failed` - these are the most important for payment integrations.\n\n## Webhook Payload\n\nEach webhook request includes:\n\n### Headers\n\n| Header | Description |\n|--------|-------------|\n| `Content-Type` | `application/json` |\n| `X-Webhook-Signature` | HMAC-SHA256 signature (hex, lowercase) |\n| `X-Webhook-Timestamp` | Unix timestamp when sent |\n\n### Body\n\n```json\n{\n  \"event\": \"invoice.paid\",\n  \"payload\": {\n    // Full invoice object with all fields\n  }\n}\n```\n\nThe `payload` contains the complete resource object, so you have all the data you need without making additional API calls.\n\nFor agreement events (`agreement.created`, `agreement.sent`, `agreement.signed`, `agreement.executed`), the payload includes the `GET /api/v1/agreements/:id` response `data` fields — including flat recipient fields such as `recipients[].email` and `recipients[].name`. Webhooks also include nested `recipients[].user` and `recipients[].contact` objects for backwards compatibility; prefer the flat fields for new integrations.\n\n## Verifying Webhooks\n\n**Always verify webhook signatures** before processing. This ensures the request actually came from Agree and wasn't tampered with.\n\n### How Verification Works\n\n1. Get the raw request body (before JSON parsing)\n2. Compute HMAC-SHA256 using your endpoint's `secret` as the key\n3. Hex-encode the result (lowercase)\n4. Compare to the `X-Webhook-Signature` header using constant-time comparison\n\n### Node.js Example\n\n```javascript\nconst crypto = require('crypto');\n\nfunction verifyWebhookSignature(rawBody, signatureHeader, secret) {\n  const expectedSignature = crypto\n    .createHmac('sha256', secret)\n    .update(rawBody)\n    .digest('hex');\n\n  return crypto.timingSafeEqual(\n    Buffer.from(expectedSignature),\n    Buffer.from(signatureHeader)\n  );\n}\n\n// Express middleware example\napp.post('/webhooks/agree', express.raw({type: 'application/json'}), (req, res) => {\n  const signature = req.headers['x-webhook-signature'];\n\n  if (!verifyWebhookSignature(req.body, signature, process.env.AGREE_WEBHOOK_SECRET)) {\n    return res.status(401).send('Invalid signature');\n  }\n\n  const event = JSON.parse(req.body);\n\n  switch (event.event) {\n    case 'invoice.paid':\n      // Handle successful payment\n      break;\n    case 'invoice.failed':\n      // Handle failed payment\n      break;\n  }\n\n  res.status(200).send('OK');\n});\n```\n\n### Python Example\n\n```python\nimport hmac\nimport hashlib\nfrom flask import Flask, request\n\napp = Flask(__name__)\n\ndef verify_webhook_signature(raw_body, signature_header, secret):\n    expected_signature = hmac.new(\n        secret.encode('utf-8'),\n        raw_body,\n        hashlib.sha256\n    ).hexdigest()\n\n    return hmac.compare_digest(expected_signature, signature_header)\n\n@app.route('/webhooks/agree', methods=['POST'])\ndef handle_webhook():\n    signature = request.headers.get('X-Webhook-Signature')\n\n    if not verify_webhook_signature(request.data, signature, AGREE_WEBHOOK_SECRET):\n        return 'Invalid signature', 401\n\n    event = request.json\n\n    if event['event'] == 'invoice.paid':\n        # Handle successful payment\n        pass\n    elif event['event'] == 'invoice.failed':\n        # Handle failed payment\n        pass\n\n    return 'OK', 200\n```\n\n## Managing Endpoints\n\n### List All Endpoints\n\n```bash\ncurl https://api.agree.com/api/v1/webhook_endpoints \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n### Update an Endpoint\n\nChange the subscribed events or URL:\n\n```bash\ncurl -X PUT https://api.agree.com/api/v1/webhook_endpoints/550e8400-e29b-41d4-a716-446655440000 \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"webhook_endpoint\": {\n      \"events\": [\"invoice.paid\", \"invoice.failed\", \"invoice.created\"]\n    }\n  }'\n```\n\n### Disable an Endpoint\n\nSet `active` to false to temporarily stop receiving webhooks:\n\n```bash\ncurl -X PUT https://api.agree.com/api/v1/webhook_endpoints/550e8400-e29b-41d4-a716-446655440000 \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"webhook_endpoint\": {\n      \"active\": false\n    }\n  }'\n```\n\n### Delete an Endpoint\n\n```bash\ncurl -X DELETE https://api.agree.com/api/v1/webhook_endpoints/550e8400-e29b-41d4-a716-446655440000 \\\n  -H \"Authorization: Bearer YOUR_API_KEY\"\n```\n\n## Retry Policy\n\nIf your endpoint returns a non-2xx status code or times out, Agree automatically retries:\n\n| Attempt | Delay |\n|---------|-------|\n| 1 | Immediate |\n| 2 | ~1 minute |\n| 3 | ~5 minutes |\n| 4 | ~30 minutes |\n| 5 | ~2 hours |\n\nAfter 5 failed attempts, the webhook is marked as failed and the endpoint's `failure_count` is incremented.\n\n**Tip:** Monitor `failure_count` to detect integration issues. If it keeps increasing, check your endpoint's logs.\n\n## Best Practices\n\n1. **Respond quickly** - Return 200 within 5 seconds, then process asynchronously\n2. **Handle duplicates** - Webhooks may occasionally be sent more than once; use idempotency\n3. **Verify signatures** - Never skip verification in production\n4. **Use HTTPS** - Required in production for security\n5. **Log everything** - Store webhook payloads for debugging and auditing\n\n## Fields Reference\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `id` | UUID | Unique endpoint identifier |\n| `url` | string | Your webhook URL (HTTPS required in production) |\n| `events` | array | Event types this endpoint receives |\n| `active` | boolean | Whether the endpoint is receiving webhooks |\n| `failure_count` | integer | Consecutive failed deliveries |\n| `secret` | string | Signing secret (only returned on creation) |\n| `inserted_at` | datetime | When the endpoint was created |\n| `updated_at` | datetime | When the endpoint was last modified |\n","name":"Webhooks"},{"description":"Read-only analytics for revenue, cashflow, and accounts receivable recovery.\n\n## Overview\n\nReport endpoints mirror the in-app **Reports** dashboards (`/reports/revenue`, `/reports/cashflow`, `/reports/recovery`). All metrics are scoped to the authenticated API key's organization.\n\nAmounts use the `Money` object shape: `amount` is in the smallest currency unit (cents for USD) and `currency` is an ISO 4217 code (e.g. `\"USD\"`).\n\n## Revenue\n\n| Endpoint | Description |\n|----------|-------------|\n| `GET /reports/revenue/stats` | ARR, MRR, growth, NRR, top-5 concentration |\n| `GET /reports/revenue/chart` | Monthly MRR/ARR history and forecast |\n| `GET /reports/revenue/customers` | Customers ranked by paid revenue |\n| `GET /reports/revenue/customers_by_mrr` | Customers ranked by subscription MRR |\n\n## Cashflow\n\n| Endpoint | Description |\n|----------|-------------|\n| `GET /reports/cashflow/stats` | Cash collected MTD, outstanding total, DSO, fail rate |\n| `GET /reports/cashflow/chart` | Monthly cash collected history and forecast |\n| `GET /reports/cashflow/outstanding_invoices` | Outstanding invoice list |\n| `GET /reports/cashflow/forecast` | Expected cash in next 30/60/90 days |\n\n## Recovery\n\n| Endpoint | Description |\n|----------|-------------|\n| `GET /reports/recovery/aging/chart` | Aging buckets by month |\n| `GET /reports/recovery/aging/invoices` | Invoices grouped by aging bucket |\n| `GET /reports/recovery/aging/trend` | Overdue totals and average days overdue |\n| `GET /reports/recovery/leakage/stats` | Counts/amounts by invoice stage |\n| `GET /reports/recovery/leakage/waterfall` | Stage waterfall for leakage analysis |\n| `GET /reports/recovery/leakage/stalled_invoices` | Stalled invoices |\n| `GET /reports/recovery/leakage/stage_durations` | Average days between stages |\n","name":"Reports"}]}