Agree API Documentation

API docs by Redocly](https://redocly.com/redoc/)

Agree API (1.0.0)

Download OpenAPI specification: Download

section/Introduction Introduction

Welcome 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.

section/Introduction/What-You-Can-Do What You Can Do

With the Agree API, you can:

section/Introduction/Quick-Start Quick Start

Here's how to send your first invoice in three API calls:

1. Get Your API Key

Generate an API key in your Agree dashboard under Settings > API Keys. Keep this key secure - it provides full access to your organization's data.

2. Create a Contact

First, add the customer you want to invoice:

curl -X POST https://api.agree.com/api/v1/contacts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contact": {
      "name": "Jane Smith",
      "email": "jane@example.com",
      "company": "Acme Corp"
    }
  }'

3. Create and Send an Invoice

Now create and send an invoice for that contact. You can use the convenience endpoint to do both in one request:

curl -X POST https://api.agree.com/api/v1/invoices/create_and_send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice": {
      "billing_contact": {
        "email": "jane@example.com"
      },
      "amount": {"amount": 10000, "currency": "USD"},
      "payment_methods": ["card", "ach"],
      "due_at": "2025-02-01T00:00:00Z",
      "memo": "Consulting services - January 2025"
    }
  }'

The 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.

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.

4. Get Notified When They Pay

Set up a webhook to know when the invoice is paid:

curl -X POST https://api.agree.com/api/v1/webhook_endpoints \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_endpoint": {
      "url": "https://your-app.com/webhooks/agree",
      "events": ["invoice.paid", "invoice.failed"]
    }
  }'

Save the secret from the response - you'll need it to verify webhook signatures.

section/Introduction/How-Resources-Connect How Resources Connect

Understanding how Agree's resources relate to each other will help you build effective integrations:

Contacts ──────► Invoices ──────► Webhooks
   │                │                 │
   │                │                 │
   ▼                ▼                 ▼
 People you      Payment           Real-time
 do business     requests          notifications
 with            you send          when things
                 to contacts       happen

section/Introduction/Base-URL Base URL

All API requests should be made to:

https://api.agree.com/api/v1

section/Introduction/Authentication Authentication

All endpoints require Bearer token authentication. Include your API key in the Authorization header:

Authorization: Bearer YOUR_API_KEY

Keep your API key secure. If compromised, regenerate it immediately in your dashboard.

section/Introduction/Request-Format Request Format

Send request bodies as JSON with the Content-Type: application/json header.

section/Introduction/Response-Format Response Format

All successful responses return JSON with a data wrapper:

{
  "data": { ... }
}

List endpoints include pagination information:

{
  "data": [ ... ],
  "pagination": {
    "page": 1,
    "page_size": 10,
    "total_pages": 5,
    "total_entries": 42
  }
}

section/Introduction/Errors Errors

Errors return appropriate HTTP status codes with details:

Status Description
400 Bad Request - Invalid parameters
401 Unauthorized - Invalid or missing API key
403 Forbidden - No access to this resource
404 Not Found - Resource doesn't exist
422 Unprocessable Entity - Validation errors

Error responses include field-specific messages:

{
  "errors": {
    "email": ["has already been taken"],
    "amount": ["must be greater than 0"]
  }
}

section/Introduction/Pagination Pagination

List endpoints support pagination with these query parameters:

Parameter Default Description
page 1 Page number to retrieve
page_size 10 Number of items per page (max: 100)

section/Introduction/Need-Help Need Help?

tag/Agreements Agreements

Create, send, and manage agreements with recipients and field assignments.

tag/Agreements/Overview Overview

Agreements 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.

Key concepts:

tag/Agreements/Creating-an-Agreement Creating an Agreement

Basic Agreement Creation

Here's a basic example of creating an agreement from a template:

curl -X POST https://api.agree.com/api/v1/agreements \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Service Agreement",
    "recipients": [\
      {\
        "contact_id": "770e8400-e29b-41d4-a716-446655440000",\
        "role": "owner"\
      },\
      {\
        "contact_id": "880e8400-e29b-41d4-a716-446655440000",\
        "role": "signer"\
      }\
    ]
  }'

The Owner Role Requirement

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.

Common mistake: If you assign yourself as a signer instead of owner, the request will fail with a validation error.

Correct approach:

{
  "recipients": [\
    {\
      "contact_id": "YOUR_CONTACT_ID",\
      "role": "owner"\
    },\
    {\
      "contact_id": "CLIENT_CONTACT_ID",\
      "role": "signer"\
    }\
  ]
}

Incorrect approach (will fail):

{
  "recipients": [\
    {\
      "contact_id": "YOUR_CONTACT_ID",\
      "role": "signer"  // ❌ Wrong - must be "owner"\
    }\
  ]
}

Finding Your Contact ID

The account holder (you) is also a contact in your organization. To find your own Contact ID:

curl https://api.agree.com/api/v1/contacts \
  -H "Authorization: Bearer YOUR_API_KEY"

This 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:

curl "https://api.agree.com/api/v1/contacts?email=your-email@example.com" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response:

{
  "data": [\
    {\
      "id": "770e8400-e29b-41d4-a716-446655440000",\
      "name": "Your Name",\
      "email": "your-email@example.com",\
      "company": "Your Company",\
      ...\
    }\
  ],
  "pagination": {
    "page": 1,
    "page_size": 10,
    "total_pages": 1,
    "total_entries": 1
  }
}

Use the id field from the contact that matches your email address as your contact_id when creating agreements.

Assigning Fields to Recipients

When creating an agreement, you can assign specific fields to specific recipients. Fields are identified by their field IDs (as defined in the template).

Example: Assigning Fields

curl -X POST https://api.agree.com/api/v1/agreements \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Service Agreement",
    "recipients": [\
      {\
        "contact_id": "770e8400-e29b-41d4-a716-446655440000",\
        "role": "owner",\
        "assigned_fields": ["company_address", "date"]\
      },\
      {\
        "contact_id": "880e8400-e29b-41d4-a716-446655440000",\
        "role": "signer",\
        "assigned_fields": ["signature_field", "date_field"]\
      }\
    ]
  }'

In this example:

Field Assignment Rules:

Complete Example with All Options:

curl -X POST https://api.agree.com/api/v1/agreements \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Service Agreement with Invoice",
    "delivery_mode": "managed",
    "field_values": {
      "field_1": "John Doe",
      "field_2": "2024-01-01"
    },
    "recipients": [\
      {\
        "contact_id": "770e8400-e29b-41d4-a716-446655440000",\
        "role": "owner",\
        "assigned_fields": ["company_address", "date"]\
      },\
      {\
        "contact": {\
          "email": "client@example.com",\
          "name": "Jane Smith",\
          "company": "Client Corp"\
        },\
        "role": "signer",\
        "assigned_fields": ["signature_field", "date_field"]\
      }\
    ],
    "signing_order_enabled": false,
    "payments_enabled": true,
    "reminder_schedule": "weekly",
    "invoice": {
      "billing_contact": {
        "email": "client@example.com",
        "name": "Jane Smith"
      },
      "amount": 15000,
      "currency": "USD",
      "memo": "Payment for services",
      "payment_methods": ["card", "ach"],
      "payment_terms_type": "net",
      "payment_terms_days": 30
    }
  }'

tag/Agreements/Daisy-Chaining:-Attaching-an-Invoice-to-an-Agreement Daisy-Chaining: Attaching an Invoice to an Agreement

There are two ways to attach an invoice to an agreement:

Option 1: Create Agreement with Invoice (Single Request)

The simplest approach is to include the invoice in the agreement creation request:

curl -X POST https://api.agree.com/api/v1/agreements \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Service Agreement with Invoice",
    "recipients": [\
      {\
        "contact_id": "YOUR_CONTACT_ID",\
        "role": "owner"\
      },\
      {\
        "contact_id": "CLIENT_CONTACT_ID",\
        "role": "signer"\
      }\
    ],
    "invoice": {
      "billing_contact": {
        "email": "client@example.com",
        "name": "Jane Smith"
      },
      "amount": 15000,
      "currency": "USD",
      "memo": "Payment for services rendered",
      "payment_methods": ["card", "ach"],
      "payment_terms_type": "net",
      "payment_terms_days": 30
    }
  }'

This 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.

Option 2: Two-Step Process (Create Agreement, Then Create Invoice)

If you need more control or want to create the invoice separately:

Step 1: Create the Agreement

curl -X POST https://api.agree.com/api/v1/agreements \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Service Agreement",
    "recipients": [\
      {\
        "contact_id": "YOUR_CONTACT_ID",\
        "role": "owner"\
      },\
      {\
        "contact_id": "CLIENT_CONTACT_ID",\
        "role": "signer"\
      }\
    ]
  }'

Response includes agreement ID:

{
  "data": {
    "id": "990e8400-e29b-41d4-a716-446655440000",
    ...
  }
}

Step 2: Create Invoice and Link to Agreement

curl -X POST https://api.agree.com/api/v1/invoices \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice": {
      "agreement_id": "990e8400-e29b-41d4-a716-446655440000",
      "billing_contact": {
        "email": "client@example.com",
        "name": "Jane Smith"
      },
      "amount": 15000,
      "currency": "USD",
      "memo": "Payment for services rendered",
      "payment_methods": ["card", "ach"],
      "payment_terms_type": "net",
      "payment_terms_days": 30
    }
  }'

When to use each approach:

tag/Agreements/Recipient-Roles Recipient Roles

Role Description
owner The account holder initiating the agreement. Exactly one recipient must have this role.
signer A recipient who needs to sign the agreement
viewer A recipient who can view but not sign the agreement
payee A recipient who will receive payment (used with invoices)

tag/Agreements/Field-Values-(Prefilling) Field Values (Prefilling)

You can prefill field values when creating an agreement:

{
  "template_id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "Service Agreement",
  "field_values": {
    "field_1": "John Doe",
    "field_2": "2024-01-01",
    "company_name": "Acme Corp"
  },
  "recipients": [...]
}

Keys 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):

curl https://api.agree.com/api/v1/agreements/templates/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer YOUR_API_KEY"

The 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).

Typed values and rich text

Each entry in field_values can be either:

  1. A string (legacy): plain text. Works for any field or variable.
  2. An object with a content string and optional content_type:
{
     "content": "<strong>Renewal</strong> 2026-05-01",
     "content_type": "html"
}

Rich text applies only to template variables. If the field_values key matches a variablefield_id (from GET /api/v1/agreements/templates/:iddata.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.).

For 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.

Invalid typed objects (for example missing content or an invalid content_type) return 400 Bad Request with an error referencing field_values.

tag/Agreements/Custom-Variables-(Template-Variables) Custom Variables (Template Variables)

Templates 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.

Variable 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.

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.

Step 1: Discover Template Variables

Fetch the template to see its variables:

curl https://api.agree.com/api/v1/agreements/templates/TEMPLATE_ID \
  -H "Authorization: Bearer YOUR_API_KEY"

Response:

{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Employment Agreement",
    "field_names": ["signature_field", "date_field"],
    "variables": [\
      {\
        "name": "Employee Name",\
        "field_id": "var_abc123"\
      },\
      {\
        "name": "Start Date",\
        "field_id": "var_def456"\
      },\
      {\
        "name": "Salary",\
        "field_id": "var_ghi789"\
      }\
    ]
  }
}

The variables array lists each custom variable with its name (display label) and field_id (the key to use in field_values).

Step 2: Provide Variable Values (Optional at Creation)

When 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.

curl -X POST https://api.agree.com/api/v1/agreements \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Employment Agreement - Jane Smith",
    "field_values": {
      "var_abc123": "Jane Smith",
      "var_def456": "2025-03-01",
      "var_ghi789": "$120,000"
    },
    "recipients": [\
      {\
        "contact_id": "YOUR_CONTACT_ID",\
        "role": "owner"\
      },\
      {\
        "contact": {\
          "email": "jane@example.com",\
          "name": "Jane Smith"\
        },\
        "role": "signer",\
        "assigned_fields": ["signature_field", "date_field"]\
      }\
    ]
  }'

When 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).

Rich text examples (variables only)

Plain string (unchanged):

"var_abc123": "Jane Smith"

HTML (lists, emphasis, etc.):

"var_schedule": {
  "content_type": "html",
  "content": "<ul><li>Payment 1 on 2026-03-17</li><li>Payment 2 on 2026-04-17</li></ul>"
}

Markdown (line breaks, - / * list lines, **bold**, *italic*, _italic_):

"var_schedule": {
  "content_type": "markdown",
  "content": "- **First** payment on 2026-03-17\n- Second payment on 2026-04-17"
}

Default to plaintext when content_type is omitted:

"var_note": { "content": "Shown as plain text only" }

Error Handling

If you attempt to send an agreement with unfilled variables, the API returns a 400 Bad Request:

{
  "error": "Unfilled variables: Employee Name, Start Date. All variables must have values before sending."
}

tag/Agreements/Listing-Agreements Listing Agreements

Retrieve agreements with optional filtering:

# Get all agreements
curl https://api.agree.com/api/v1/agreements \
  -H "Authorization: Bearer YOUR_API_KEY"

# Filter by status
curl "https://api.agree.com/api/v1/agreements?status=drafted" \
  -H "Authorization: Bearer YOUR_API_KEY"

Query Parameters

Parameter Type Description
page integer Page number (default: 1)
page_size integer Items per page (default: 10, max: 100)
status string Filter by status: created, drafted, sent, signed, executed, terminated

tag/Agreements/Sending-an-Agreement Sending an Agreement

After creating an agreement, send it to recipients:

curl -X POST https://api.agree.com/api/v1/agreements/990e8400-e29b-41d4-a716-446655440000/send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "delivery_mode": "managed"
  }'

Delivery Modes:

tag/Agreements/Create-and-Send-in-One-Step Create and Send in One Step

For convenience, create and send an agreement in a single request:

curl -X POST https://api.agree.com/api/v1/agreements/create_and_send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Service Agreement",
    "delivery_mode": "managed",
    "recipients": [\
      {\
        "contact_id": "YOUR_CONTACT_ID",\
        "role": "owner"\
      },\
      {\
        "contact_id": "CLIENT_CONTACT_ID",\
        "role": "signer"\
      }\
    ]
  }'

tag/Agreements/Updating-an-Agreement Updating an Agreement

Update 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.

curl -X PUT https://api.agree.com/api/v1/agreements/990e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agreement": {
      "name": "Updated Service Agreement",
      "recipients": [\
        {\
          "contact_id": "YOUR_CONTACT_ID",\
          "role": "owner",\
          "assigned_fields": ["company_address"]\
        },\
        {\
          "contact_id": "CLIENT_CONTACT_ID",\
          "role": "signer",\
          "assigned_fields": ["signature_field"]\
        }\
      ]
    }
  }'

Note: Updating recipients replaces all existing recipients. Make sure to include all recipients you want to keep.

tag/Agreements/Agreement-Statuses Agreement Statuses

Status Description
created Agreement created but not yet finalized
drafted Agreement is in draft state (default when created)
sent Agreement has been sent to recipients
viewed At least one recipient has viewed the agreement
signed At least one recipient has signed
executed Agreement is fully executed (all required signatures collected)
renewed Agreement has been renewed
terminated Agreement has been terminated

tag/Agreements/Fields-Reference Fields Reference

Core Fields

Field Type Description
id UUID Unique agreement identifier
name string Agreement name
status string Current status (see statuses above)
template_id UUID Template used to create this agreement
organization_id UUID Your organization's ID
invoice_template_id UUID Associated invoice template (if invoice was created)

Recipient Fields

Field Type Description
recipients array List of recipients with their roles and assigned fields
signing_order array List of recipient IDs in signing order (if enabled)
signing_order_enabled boolean Whether signing order is enforced

Delivery Fields

Field Type Description
delivery_mode string embedded or managed
reminder_schedule string none, daily, weekly, or monthly
reminder_scheduled_at datetime When the next reminder will be sent

Date Fields

Field Type Description
starts_at datetime When the agreement starts
ends_at datetime When the agreement ends
executed_at datetime When the agreement was fully executed
last_reminder_sent_at datetime When the last reminder was sent

tag/Agreements/operation/AgreeWeb.API.V1.AgreementController.pdf Download agreement PDF

Returns a presigned URL to download the agreement PDF for the current revision (same source as the Agree app document menu).

If the PDF is not in storage yet or is stale vs. the revision, the server first waits briefly in case another request already kicked off generation, then may enqueue SSR rendering and waits up to a short inline budget (default 5 seconds, invoice_pdf_api_inline_wait_ms). If the file is still not ready, responds with 202 Accepted, a Retry-After header (default 3 seconds, invoice_pdf_api_retry_after_seconds), and data.status: "pending". Repeat the same GET until you receive 200 with data.url.

Authorizations:

bearer

path Parameters
id
required
string
Agreement ID (UUID)

Responses

200

Presigned download URL

202

PDF not ready; retry after Retry-After

400

Bad request

401

Unauthorized

403

Forbidden

404

Not found

get/api/v1/agreements/{id}/pdf

https://secure.agree.com/api/v1/agreements/{id}/pdf

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"expires_in": 0,

"url": "http://example.com"

}

}`

tag/Agreements/operation/AgreeWeb.API.V1.AgreementController.send Send agreement

Sends an agreement to its recipients. Updates the agreement status to 'sent' and sends emails if delivery_mode is 'managed'.

Authorizations:

bearer

path Parameters
id
required
string
Agreement ID (UUID)
Request Body schema: application/json optional

Send params

delivery_method string
Value:"email"
Delivery method (only valid for managed mode)
delivery_mode
required
string
Enum:"embedded""managed"
Delivery mode: 'embedded' (emails suppressed) or 'managed' (Agree sends emails)
message string or null
Optional message to include in email (only valid for managed mode)
reminder_schedule string or null
Enum:"none""daily""weekly""monthly"
Reminder schedule override (only valid for managed mode)

Responses

200

Agreement sent

400

Bad request

401

Unauthorized

403

Forbidden

404

Not found

post/api/v1/agreements/{id}/send

https://secure.agree.com/api/v1/agreements/{id}/send

Request samples

Content type

application/json

Copy

`{"delivery_method": "email",

"delivery_mode": "managed",

"message": "Please review and sign this agreement",

"reminder_schedule": "weekly"

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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

}

}`

tag/Agreements/operation/AgreeWeb.API.V1.AgreementController.create_and_send Create and send agreement

Convenience endpoint that creates an agreement from a template and sends it immediately.

Combines create and send operations in a single request.

Authorizations:

bearer

Request Body schema: application/json optional

Create and send params

delivery_mode string
Enum:"embedded""managed"
Delivery mode: 'embedded' (emails suppressed) or 'managed' (Agree sends emails)
ends_at string or null
When the agreement ends (ISO8601 format)
field_values object
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.
invoice object or null
Invoice to create with this agreement. Creates an invoice template associated with the agreement.
Either billing_contact or contact_id is required when providing an invoice.
Either amount/currency or line_items is required (if line_items are provided, amount is calculated from them).
IMPORTANT: 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.
name
required
string
Agreement name
payments_enabled boolean
Whether payments are enabled for this agreement
recipients Array of objects
List of recipients with their assigned fields.
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.
Each recipient must provide either contact_id or contact (but not both).
- contact_id: Reference an existing contact
- contact: Create or update a contact with email and name (required), and optionally company and title
Use assigned_fields to assign specific fields (by field name) to each recipient. Fields not assigned will default to the owner recipient.
reminder_schedule string or null
Enum:"none""daily""weekly""monthly"
Reminder schedule frequency (only valid for managed mode)
signing_order Array of strings [ items ]
List of contact IDs in signing order
signing_order_enabled boolean
Whether signing order is enabled
starts_at string or null
When the agreement starts (ISO8601 format)
template_id
required
string
Template ID to create agreement from (required)

Responses

201

Agreement created and sent

401

Unauthorized

404

Template not found

422

Validation errors

post/api/v1/agreements/create_and_send

https://secure.agree.com/api/v1/agreements/create\_and\_send

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"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"

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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,

"signing_order": [ ],

"signing_order_enabled": false,

"starts_at": "2024-01-01T00:00:00Z",

"status": "drafted",

"version": 0

}

}`

tag/Agreements/operation/AgreeWeb.API.V1.AgreementController.templates List agreement templates

Returns a list of agreement templates for the authenticated organization with field names.

Authorizations:

bearer

Responses

200

Templates list

401

Unauthorized

get/api/v1/agreements/templates

https://secure.agree.com/api/v1/agreements/templates

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{"field_names": ["signature_field",

"date_field",

"name_field"

],

"id": "550e8400-e29b-41d4-a716-446655440000",

"name": "Service Agreement Template"

}

]

}`

tag/Agreements/operation/AgreeWeb.API.V1.AgreementController.index List agreements

Returns a paginated list of agreements for the authenticated organization.

Authorizations:

bearer

query Parameters
page integer
Page number (default: 1)
page_size integer
Items per page (default: 10)

Responses

200

Agreements list

400

Bad Request

401

Unauthorized

get/api/v1/agreements

https://secure.agree.com/api/v1/agreements

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{"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

}

],

"pagination": {"page": 0,

"page_size": 0,

"total_entries": 0,

"total_pages": 0

}

}`

tag/Agreements/operation/AgreeWeb.API.V1.AgreementController.create Create agreement from template

Creates a new agreement from a template for the authenticated organization.

The agreement will be created with status 'drafted' by default. Requires a template_id and supports prefilling fields via field_values mapping.

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.

For each recipient, you can optionally provide either:

Both will set the recipient's contact automatically. You cannot provide both for the same recipient.

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.

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).

field_values: Prefills values in the document without changing who each field is assigned to.

Authorizations:

bearer

Request Body schema: application/json optional

Agreement create params

Responses

201

Agreement created

401

Unauthorized

404

Template not found

422

Validation errors

post/api/v1/agreements

https://secure.agree.com/api/v1/agreements

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"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,

"reminder_schedule": "weekly",

"signing_order_enabled": false,

"template_id": "550e8400-e29b-41d4-a716-446655440000"

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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,

"signing_order": [ ],

"signing_order_enabled": false,

"starts_at": "2024-01-01T00:00:00Z",

"status": "drafted",

"version": 0

}

}`

tag/Agreements/operation/AgreeWeb.API.V1.AgreementController.show_template Get template

Returns a single template by ID with field names.

Authorizations:

bearer

path Parameters
id
required
string
Template ID (UUID)

Responses

200

Template

401

Unauthorized

403

Forbidden

404

Not found

get/api/v1/agreements/templates/{id}

https://secure.agree.com/api/v1/agreements/templates/{id}

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"field_names": ["signature_field",

"date_field",

"name_field"

],

"id": "550e8400-e29b-41d4-a716-446655440000",

"name": "Service Agreement Template"

}

}`

tag/Agreements/operation/AgreeWeb.API.V1.AgreementController.delete Delete agreement

Deletes an agreement by ID (soft delete).

Authorizations:

bearer

path Parameters
id
required
string
Agreement ID (UUID)

Responses

204

Agreement deleted

401

Unauthorized

403

Forbidden

404

Not found

delete/api/v1/agreements/{id}

https://secure.agree.com/api/v1/agreements/{id}

Response samples

Content type

application/json

Copy

`{"error": "Invalid or missing API key"

}`

tag/Agreements/operation/AgreeWeb.API.V1.AgreementController.show Get agreement

Returns a single agreement by ID.

Authorizations:

bearer

path Parameters
id
required
string
Agreement ID (UUID)

Responses

200

Agreement

401

Unauthorized

403

Forbidden

404

Not found

get/api/v1/agreements/{id}

https://secure.agree.com/api/v1/agreements/{id}

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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,

"signing_order": [ ],

"signing_order_enabled": false,

"starts_at": "2024-01-01T00:00:00Z",

"status": "drafted",

"version": 0

}

}`

tag/Agreements/operation/AgreeWeb.API.V1.AgreementController.update (2) Update agreement

Updates an existing agreement.

The following fields cannot be updated directly:

Authorizations:

bearer

path Parameters
id
required
string
Agreement ID (UUID)
Request Body schema: application/json optional

Agreement params

agreement
required
object

Responses

200

Agreement updated

401

Unauthorized

403

Forbidden

404

Not found

422

Validation errors

patch/api/v1/agreements/{id}

https://secure.agree.com/api/v1/agreements/{id}

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"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"

}

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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,

"signing_order": [ ],

"signing_order_enabled": false,

"starts_at": "2024-01-01T00:00:00Z",

"status": "drafted",

"version": 0

}

}`

tag/Agreements/operation/AgreeWeb.API.V1.AgreementController.update Update agreement

Updates an existing agreement.

The following fields cannot be updated directly:

Authorizations:

bearer

path Parameters
id
required
string
Agreement ID (UUID)
Request Body schema: application/json optional

Agreement params

agreement
required
object

Responses

200

Agreement updated

401

Unauthorized

403

Forbidden

404

Not found

422

Validation errors

put/api/v1/agreements/{id}

https://secure.agree.com/api/v1/agreements/{id}

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"agreement": {"ends_at": "2024-12-31T23:59:59Z",

"forward_signature_enabled": true,

"name": "Service Agreement",

"payments_enabled": false,

"reminder_schedule": "weekly",

"signing_order": [ ],

"signing_order_enabled": false,

"starts_at": "2024-01-01T00:00:00Z"

}

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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,

"signing_order": [ ],

"signing_order_enabled": false,

"starts_at": "2024-01-01T00:00:00Z",

"status": "drafted",

"version": 0

}

}`

tag/Invoices Invoices

Create, send, and track payment requests to your customers.

tag/Invoices/Overview Overview

Invoices 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.

Key concepts:

tag/Invoices/Common-Use-Cases Common Use Cases

Bill a Client for a Completed Project

Send a one-time invoice after completing work:

curl -X POST https://api.agree.com/api/v1/invoices \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice": {
      "billing_contact": {
        "email": "client@company.com",
        "name": "Sarah Johnson",
        "company": "Johnson & Co"
      },
      "amount": {"amount": 500000, "currency": "USD"},
      "payment_methods": ["card", "ach", "wire"],
      "due_at": "2025-02-01T00:00:00Z",
      "memo": "Website redesign project - Final payment"
    }
  }'

The client receives an email with a payment link. You'll get a webhook when they pay.

Set Up Monthly Retainer Billing

Create a recurring invoice that bills automatically each month:

curl -X POST https://api.agree.com/api/v1/invoices \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice": {
      "billing_contact": {
        "email": "accounting@bigcorp.com",
        "name": "Accounts Payable",
        "company": "BigCorp Inc"
      },
      "amount": {"amount": 250000, "currency": "USD"},
      "payment_methods": ["ach"],
      "memo": "Monthly consulting retainer",
      "recurring_options": {
        "schedule": "custom",
        "repeat_frequency": 1,
        "repeat_unit": "month",
        "repeat_on_type": "day_of_month",
        "repeat_on_day": 1,
        "recurring_end_type": "never",
        "reminder_schedule": "weekly"
      }
    }
  }'

Agree automatically generates and sends invoices on the 1st of each month.

Track Outstanding Invoices

Find all unpaid invoices that are past due:

curl "https://api.agree.com/api/v1/invoices?statuses=sent,due&date_type=due_at&date_end=2025-01-17" \
  -H "Authorization: Bearer YOUR_API_KEY"

Handle Failed Payments

When 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:

curl "https://api.agree.com/api/v1/invoices?statuses=failed" \
  -H "Authorization: Bearer YOUR_API_KEY"

Generate a Revenue Report

Get all paid invoices for a specific month:

curl "https://api.agree.com/api/v1/invoices?statuses=paid&date_type=paid_at&date_start=2025-01-01&date_end=2025-01-31" \
  -H "Authorization: Bearer YOUR_API_KEY"

tag/Invoices/Creating-an-Invoice Creating an Invoice

Here's a basic invoice creation:

curl -X POST https://api.agree.com/api/v1/invoices \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice": {
      "billing_contact": {
        "email": "customer@example.com",
        "name": "John Doe",
        "company": "Acme Corp"
      },
      "amount": {"amount": 15000, "currency": "USD"},
      "payment_methods": ["card", "ach"],
      "due_at": "2025-02-15T00:00:00Z",
      "scheduled_at": "2025-02-01T00:00:00Z",
      "memo": "Website development - Phase 1"
    }
  }'

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).

Response:

{
  "data": {
    "id": "4a755746-ba45-4226-a669-aebc7ad3719c",
    "status": "sent",
    "amount": {"amount": 15000, "currency": "USD"},
    "billing_contact": {
      "email": "customer@example.com",
      "name": "John Doe",
      "company": "Acme Corp",
      "title": null
    },
    "payment_link": "https://agree.com/pay/abc123token",
    "payment_methods": ["card", "ach"],
    "due_at": "2025-02-15T00:00:00Z",
    "scheduled_at": "2025-02-01T00:00:00Z",
    "memo": "Website development - Phase 1",
    "inserted_at": "2025-01-15T10:30:00Z"
  }
}

The 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.

Create and Send in One Step

For convenience, you can create and send an invoice in a single API call:

curl -X POST https://api.agree.com/api/v1/invoices/create_and_send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice": {
      "billing_contact": {
        "email": "customer@example.com",
        "name": "John Doe"
      },
      "amount": {"amount": 15000, "currency": "USD"},
      "payment_methods": ["card", "ach"],
      "memo": "Website development - Phase 1"
    }
  }'

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:

curl -X POST https://api.agree.com/api/v1/invoices/create_and_send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice": {
      "billing_contact": {
        "email": "customer@example.com",
        "name": "John Doe"
      },
      "amount": {"amount": 15000, "currency": "USD"},
      "payment_methods": ["card", "ach"],
      "due_at": "2025-02-15T00:00:00Z",
      "memo": "Website development - Phase 1"
    }
  }'

Note 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.

Response:

{
  "data": {
    "id": "4a755746-ba45-4226-a669-aebc7ad3719c",
    "status": "sending",
    "amount": {"amount": 15000, "currency": "USD"},
    ...
  }
}

The response includes status: "sending" to indicate the invoice is being sent asynchronously. The invoice will transition to sent once the email is delivered.

When to use create_and_send:

When to use create + send separately:

Amounts

Amounts are specified in the smallest currency unit. For USD, this means cents:

You want to charge Send this amount
$100.00 10000
$1,500.50 150050
$0.99 99
{
  "amount": {
    "amount": 10000,
    "currency": "USD"
  }
}

Specifying the Customer

You can specify who receives the invoice in two ways:

Using billing_contact (recommended for new customers):

{
  "invoice": {
    "billing_contact": {
      "email": "customer@example.com",
      "name": "John Doe"
    }
  }
}

This creates or updates a contact automatically.

Using contact_id (for existing contacts):

{
  "invoice": {
    "contact_id": "550e8400-e29b-41d4-a716-446655440000"
  }
}

You cannot use both in the same request.

tag/Invoices/Invoice-Lifecycle Invoice Lifecycle

Every invoice progresses through a series of statuses:

┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐
│ created │ ──► │  sent   │ ──► │   due   │ ──► │  paid   │
└─────────┘     └─────────┘     └─────────┘     └─────────┘
                                     │
                                     ├──► processing ──► paid
                                     │                    │
                                     │                    └──► failed
                                     │
                                     └──► canceled
Status Description
created Invoice created but not yet sent to customer
sending Invoice is being sent (temporary status returned by API)
sent Invoice emailed to customer, awaiting payment
due Invoice is past the scheduled send date
processing Payment initiated, waiting for confirmation
paid Payment completed successfully
failed Payment attempt failed (customer can retry)
canceled Invoice was canceled (no payment expected)
refunded Payment was refunded after completion
draft Template-only, not yet converted to invoice

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).

tag/Invoices/Recurring-Invoices Recurring Invoices

Set up automatic recurring invoices by providing recurring_options:

curl -X POST https://api.agree.com/api/v1/invoices \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice": {
      "billing_contact": {"email": "customer@example.com"},
      "amount": {"amount": 99900, "currency": "USD"},
      "payment_methods": ["card"],
      "recurring_options": {
        "schedule": "custom",
        "repeat_frequency": 1,
        "repeat_unit": "month",
        "recurring_end_type": "never",
        "reminder_schedule": "weekly"
      }
    }
  }'

Recurring Options

Field Type Description
schedule string none (one-time) or custom (recurring)
repeat_frequency integer How often to repeat (e.g., 1 = every period, 2 = every other)
repeat_unit string week or month
repeat_on_weekday string For weekly: monday, tuesday, etc.
repeat_on_type string For monthly: day_of_month or day_of_week
repeat_on_day integer Day of month (1-31)
repeat_on_week integer Week of month (1-5, where 5 = last)
recurring_end_type string never, date, or count
recurring_end_date datetime End date (when type is date)
recurring_end_count integer Number of occurrences (when type is count)
reminder_schedule string none, daily, weekly, or monthly
forward_payment_enabled boolean Allow paying future invoices early
pass_on_fees_enabled boolean Pass processing fees to the payer at checkout (default: false)

Examples

Monthly on the 15th, forever:

{
  "schedule": "custom",
  "repeat_frequency": 1,
  "repeat_unit": "month",
  "repeat_on_type": "day_of_month",
  "repeat_on_day": 15,
  "recurring_end_type": "never"
}

Every 2 weeks on Monday, for 6 occurrences:

{
  "schedule": "custom",
  "repeat_frequency": 2,
  "repeat_unit": "week",
  "repeat_on_weekday": "monday",
  "recurring_end_type": "count",
  "recurring_end_count": 6
}

tag/Invoices/Listing-and-Filtering-Invoices Listing and Filtering Invoices

Retrieve invoices with powerful filtering options:

# Get all invoices
curl https://api.agree.com/api/v1/invoices \
  -H "Authorization: Bearer YOUR_API_KEY"

# Filter by status
curl "https://api.agree.com/api/v1/invoices?statuses=sent,due" \
  -H "Authorization: Bearer YOUR_API_KEY"

# Filter by date range (invoices due in January 2025)
curl "https://api.agree.com/api/v1/invoices?date_type=due_at&date_start=2025-01-01&date_end=2025-01-31" \
  -H "Authorization: Bearer YOUR_API_KEY"

# Filter by amount range ($100-$500)
curl "https://api.agree.com/api/v1/invoices?amount_min=100&amount_max=500" \
  -H "Authorization: Bearer YOUR_API_KEY"

Query Parameters

Parameter Type Description
page integer Page number (default: 1)
page_size integer Items per page (default: 10, max: 100)
statuses string Comma-separated status filter
date_start string Start date (YYYY-MM-DD)
date_end string End date (YYYY-MM-DD)
date_type string Which date to filter: paid_at, due_at, scheduled_at
date_timezone string Timezone for dates (default: Etc/UTC)
amount_min number Minimum amount in dollars
amount_max number Maximum amount in dollars
customer string Filter by customer/company name
include_drafts boolean Include draft invoices

tag/Invoices/Sending-an-Invoice Sending an Invoice

If you created an invoice without sending it (or want to resend), you can send it explicitly:

curl -X POST https://api.agree.com/api/v1/invoices/4a755746-ba45-4226-a669-aebc7ad3719c/send \
  -H "Authorization: Bearer YOUR_API_KEY"

Response:

{
  "data": {
    "id": "4a755746-ba45-4226-a669-aebc7ad3719c",
    "status": "sending",
    ...
  }
}

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.

tag/Invoices/Updating-an-Invoice Updating an Invoice

Update invoice details before payment:

curl -X PUT https://api.agree.com/api/v1/invoices/4a755746-ba45-4226-a669-aebc7ad3719c \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "invoice": {
      "memo": "Updated memo - Website development Phase 1",
      "due_at": "2025-02-28T00:00:00Z"
    }
  }'

Note: Some fields cannot be changed after certain status transitions (e.g., you can't change the amount after payment processing begins).

tag/Invoices/Downloading-invoice-and-receipt-PDFs Downloading invoice and receipt PDFs

These 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).

Invoice PDF

curl "https://api.agree.com/api/v1/invoices/INVOICE_ID/pdf" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200):

{
  "data": {
    "url": "https://...",
    "expires_in": 3600
  }
}

If 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.

Receipt PDF

Only available when the invoice status is paid. Otherwise the API returns 422.

curl "https://api.agree.com/api/v1/invoices/INVOICE_ID/receipt_pdf" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response (200): Same shape as invoice PDF (data.url, data.expires_in).

If the invoice is paid but the receipt file is not available yet, the API returns 404.

tag/Invoices/Canceling-an-Invoice Canceling an Invoice

Cancel an unpaid invoice:

curl -X DELETE https://api.agree.com/api/v1/invoices/4a755746-ba45-4226-a669-aebc7ad3719c \
  -H "Authorization: Bearer YOUR_API_KEY"

tag/Invoices/Fields-Reference Fields Reference

Core Fields

Field Type Description
id UUID Unique invoice identifier
name string Invoice display name
status string Current status (see lifecycle)
amount object Amount with amount (cents) and currency
memo string Notes visible to customer (max 255 chars)
organization_id UUID Your organization's ID
agreement_id UUID Associated agreement (if any)

Customer Fields

Field Type Description
billing_contact object Customer info: email, name, company, title
payment_link string URL where customer can pay

Payment Fields

Field Type Description
payment_methods array Accepted methods: ach, card, wire
payment_type string invoice, payment, or subscription
used_payment_method string Method used for successful payment
sales_tax_percentage number Tax percentage applied

Date Fields

Field Type Description
scheduled_at datetime When invoice will be/was sent
sent_at datetime When invoice was emailed
due_at datetime Payment due date
paid_at datetime When payment completed
processing_at datetime When processing started
authorized_at datetime When payment was authorized
inserted_at datetime When invoice was created

Delivery Fields

Field Type Description
delivery_method string How invoice is delivered (email)
automatic_delivery boolean Auto-send when created

Recurring Fields

Field Type Description
recurring_options object Recurring schedule configuration
recurring_sequence integer Position in recurring series (1, 2, 3...)

Reminder Fields

Field Type Description
reminder_scheduled_at datetime Next reminder date
last_reminder_sent_at datetime Last reminder sent

External Reference Fields

Field Type Description
external_id string Your external invoice ID
external_customer_id string Your external customer ID
destination_organization_id UUID For B2B: receiving organization

tag/Invoices/operation/AgreeWeb.API.V1.InvoiceController.receipt_pdf Download receipt PDF

Returns a presigned URL to download the payment receipt PDF for a paid invoice (same as the Agree app invoice menu). Returns 422 if the invoice is not paid.

Authorizations:

bearer

path Parameters
id
required
string
Invoice ID (UUID)

Responses

200

Presigned download URL

400

Bad request

401

Unauthorized

403

Forbidden

404

Receipt PDF not found

422

Invoice not paid

get/api/v1/invoices/{id}/receipt_pdf

https://secure.agree.com/api/v1/invoices/{id}/receipt\_pdf

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"expires_in": 0,

"url": "http://example.com"

}

}`

tag/Invoices/operation/AgreeWeb.API.V1.InvoiceController.mark_as_sent Mark invoice as sent

Marks an invoice as sent with optional sent date.

Authorizations:

bearer

path Parameters
id
required
string
Invoice ID (UUID)
Request Body schema: application/json optional

Mark as sent params

sent_at string
Date when invoice was sent (ISO8601 format: YYYY-MM-DD). Defaults to today.

Responses

200

Invoice marked as sent

401

Unauthorized

403

Forbidden

404

Not found

post/api/v1/invoices/{id}/mark_as_sent

https://secure.agree.com/api/v1/invoices/{id}/mark\_as\_sent

Request samples

Content type

application/json

Copy

`{"sent_at": "2019-08-24"

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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

}

}`

tag/Invoices/operation/AgreeWeb.API.V1.InvoiceController.send Send invoice

Sends an invoice to its recipient. Schedules the invoice for sending via the invoice scheduler, which will send email notifications to the recipient.

Authorizations:

bearer

path Parameters
id
required
string
Invoice ID (UUID)

Responses

200

Invoice sent

400

Bad request

401

Unauthorized

403

Forbidden

404

Not found

post/api/v1/invoices/{id}/send

https://secure.agree.com/api/v1/invoices/{id}/send

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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

}

}`

tag/Invoices/operation/AgreeWeb.API.V1.InvoiceController.index List invoices

Returns a paginated list of invoices for the authenticated organization.

Authorizations:

bearer

query Parameters
page integer
Page number (default: 1)
page_size integer
Items per page (default: 10)
date_start string
Start date for filtering (ISO8601 format: YYYY-MM-DD)
date_end string
End date for filtering (ISO8601 format: YYYY-MM-DD)
date_type string
Date field to filter by. Valid values: paid_at, due_at, scheduled_at (default: scheduled_at)
date_timezone string
Timezone for date filtering (default: Etc/UTC)
statuses string
Filter by invoice statuses (comma-separated). Valid values: created, due, sent, canceled, paid, failed, refunded, draft
amount_min number
Minimum invoice amount (in dollars)
amount_max number
Maximum invoice amount (in dollars)
customer string
Filter by customer/company name (fuzzy search)
include_drafts boolean
Include draft invoices (invoice templates without invoices)

Responses

200

Invoices list

401

Unauthorized

get/api/v1/invoices

https://secure.agree.com/api/v1/invoices

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{"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

}

],

"pagination": {"page": 0,

"page_size": 0,

"total_entries": 0,

"total_pages": 0

}

}`

tag/Invoices/operation/AgreeWeb.API.V1.InvoiceController.create Create invoice

Creates a new invoice for the authenticated organization.

Amounts 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.

Required fields:

You can optionally provide:

You can optionally provide either:

Both will set the invoice's billing_contact automatically. You cannot provide both.

Authorizations:

bearer

Request Body schema: application/json optional

Invoice params

invoice
required
object

Responses

201

Invoice created

401

Unauthorized

422

Validation errors

post/api/v1/invoices

https://secure.agree.com/api/v1/invoices

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"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"

}

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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

}

}`

tag/Invoices/operation/AgreeWeb.API.V1.InvoiceController.pdf Download invoice PDF

Returns a presigned URL to download the invoice PDF (same source as the Agree app invoice menu).

If the PDF is not in storage yet, the server first waits briefly in case another request already kicked off generation, then may enqueue generation and waits up to a short inline budget (default 5 seconds, invoice_pdf_api_inline_wait_ms). If the file is still not ready, responds with 202 Accepted, a Retry-After header (default 3 seconds, invoice_pdf_api_retry_after_seconds), and data.status: "pending". Repeat the same GET until you receive 200 with data.url.

Authorizations:

bearer

path Parameters
id
required
string
Invoice ID (UUID)

Responses

200

Presigned download URL

202

PDF not ready; retry after Retry-After

400

Bad request

401

Unauthorized

403

Forbidden

404

Not found

get/api/v1/invoices/{id}/pdf

https://secure.agree.com/api/v1/invoices/{id}/pdf

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"expires_in": 0,

"url": "http://example.com"

}

}`

tag/Invoices/operation/AgreeWeb.API.V1.InvoiceController.delete Delete invoice

Deletes an invoice by ID.

Authorizations:

bearer

path Parameters
id
required
string
Invoice ID (UUID)

Responses

204

Invoice deleted

401

Unauthorized

403

Forbidden

404

Not found

delete/api/v1/invoices/{id}

https://secure.agree.com/api/v1/invoices/{id}

Response samples

Content type

application/json

Copy

`{"error": "Invalid or missing API key"

}`

tag/Invoices/operation/AgreeWeb.API.V1.InvoiceController.show Get invoice

Returns a single invoice by ID.

Authorizations:

bearer

path Parameters
id
required
string
Invoice ID (UUID)

Responses

200

Invoice

401

Unauthorized

403

Forbidden

404

Not found

get/api/v1/invoices/{id}

https://secure.agree.com/api/v1/invoices/{id}

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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

}

}`

tag/Invoices/operation/AgreeWeb.API.V1.InvoiceController.update (2) Update invoice

Updates an existing invoice.

Authorizations:

bearer

path Parameters
id
required
string
Invoice ID (UUID)
Request Body schema: application/json optional

Invoice params

invoice
required
object

Responses

200

Invoice updated

401

Unauthorized

403

Forbidden

404

Not found

422

Validation errors

patch/api/v1/invoices/{id}

https://secure.agree.com/api/v1/invoices/{id}

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"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"

}

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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

}

}`

tag/Invoices/operation/AgreeWeb.API.V1.InvoiceController.update Update invoice

Updates an existing invoice.

Authorizations:

bearer

path Parameters
id
required
string
Invoice ID (UUID)
Request Body schema: application/json optional

Invoice params

invoice
required
object

Responses

200

Invoice updated

401

Unauthorized

403

Forbidden

404

Not found

422

Validation errors

put/api/v1/invoices/{id}

https://secure.agree.com/api/v1/invoices/{id}

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"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"

}

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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

}

}`

tag/Invoices/operation/AgreeWeb.API.V1.InvoiceController.create_and_send Create and send invoice

Convenience endpoint that creates an invoice and sends it immediately.

Combines create and send operations in a single request. The invoice will be scheduled for sending via the invoice scheduler, which will send email notifications to the recipient.

Authorizations:

bearer

Request Body schema: application/json optional

Invoice params

invoice
required
object

Responses

201

Invoice created and sent

401

Unauthorized

422

Validation errors

post/api/v1/invoices/create_and_send

https://secure.agree.com/api/v1/invoices/create\_and\_send

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"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"

}

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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

}

}`

tag/Invoices/operation/AgreeWeb.API.V1.InvoiceController.mark_as_paid Mark invoice as paid

Marks an invoice as paid with optional paid date and payment method.

Authorizations:

bearer

path Parameters
id
required
string
Invoice ID (UUID)
Request Body schema: application/json optional

Mark as paid params

paid_at string
Date when invoice was paid (ISO8601 format: YYYY-MM-DD). Defaults to today.
payment_method string
Enum:"manual""ach""card""wire"
Payment method used (default: manual)

Responses

200

Invoice marked as paid

401

Unauthorized

403

Forbidden

404

Not found

post/api/v1/invoices/{id}/mark_as_paid

https://secure.agree.com/api/v1/invoices/{id}/mark\_as\_paid

Request samples

Content type

application/json

Copy

`{"paid_at": "2019-08-24",

"payment_method": "manual"

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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

}

}`

tag/Contacts Contacts

Manage your organization's contacts - the people and companies you do business with.

tag/Contacts/Overview Overview

Contacts 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.

Key concepts:

tag/Contacts/Creating-a-Contact Creating a Contact

To add a new contact to your address book:

curl -X POST https://api.agree.com/api/v1/contacts \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contact": {
      "name": "Jane Smith",
      "email": "jane@acme.com",
      "company": "Acme Corporation",
      "title": "CFO"
    }
  }'

Response:

{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Jane Smith",
    "email": "jane@acme.com",
    "company": "Acme Corporation",
    "title": "CFO",
    "address": null,
    "organization_id": "660e8400-e29b-41d4-a716-446655440000",
    "inserted_at": "2025-01-15T10:30:00Z",
    "updated_at": "2025-01-15T10:30:00Z"
  }
}

tag/Contacts/Using-Contacts-with-Invoices Using Contacts with Invoices

Once you have a contact, you can reference them when creating invoices. There are two ways to associate a contact with an invoice:

Option 1: Use contact_id

If you already have a contact, pass their ID:

{
  "invoice": {
    "contact_id": "550e8400-e29b-41d4-a716-446655440000",
    "amount": {"amount": 10000, "currency": "USD"}
  }
}

Option 2: Use billing_contact

Pass contact details directly - this will find or create the contact automatically:

{
  "invoice": {
    "billing_contact": {
      "email": "jane@acme.com",
      "name": "Jane Smith",
      "company": "Acme Corporation"
    },
    "amount": {"amount": 10000, "currency": "USD"}
  }
}

If a contact with that email already exists, their details will be updated. If not, a new contact is created.

tag/Contacts/Listing-and-Filtering-Contacts Listing and Filtering Contacts

Retrieve contacts with optional filtering:

# Get all contacts
curl https://api.agree.com/api/v1/contacts \
  -H "Authorization: Bearer YOUR_API_KEY"

# Search by email
curl "https://api.agree.com/api/v1/contacts?email=jane" \
  -H "Authorization: Bearer YOUR_API_KEY"

# Filter by company
curl "https://api.agree.com/api/v1/contacts?company=acme" \
  -H "Authorization: Bearer YOUR_API_KEY"

Query Parameters

Parameter Type Description
page integer Page number (default: 1)
page_size integer Items per page (default: 10, max: 100)
email string Filter by email address (fuzzy search)
company string Filter by company name (fuzzy search)

tag/Contacts/Updating-a-Contact Updating a Contact

Update contact details using PUT:

curl -X PUT https://api.agree.com/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contact": {
      "title": "CEO",
      "company": "Acme Corp International"
    }
  }'

tag/Contacts/Deleting-a-Contact Deleting a Contact

Delete a contact by ID:

curl -X DELETE https://api.agree.com/api/v1/contacts/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer YOUR_API_KEY"

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.

tag/Contacts/Fields-Reference Fields Reference

Field Type Description
id UUID Unique contact identifier
name string Contact's full name (required)
email string Contact's email address (required, unique per organization)
company string Company or organization name
title string Job title or role
address string Mailing address
organization_id UUID Your organization's ID
inserted_at datetime When the contact was created
updated_at datetime When the contact was last updated

tag/Contacts/operation/AgreeWeb.API.V1.ContactController.index List contacts

Returns a paginated list of contacts for the authenticated organization.

Authorizations:

bearer

query Parameters
page integer
Page number (default: 1)
page_size integer
Items per page (default: 10)
email string
Filter by email (fuzzy search)
company string
Filter by company name (fuzzy search)

Responses

200

Contacts list

400

Bad Request

401

Unauthorized

get/api/v1/contacts

https://secure.agree.com/api/v1/contacts

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{"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"

}

],

"pagination": {"page": 0,

"page_size": 0,

"total_entries": 0,

"total_pages": 0

}

}`

tag/Contacts/operation/AgreeWeb.API.V1.ContactController.create Create contact

Creates a new contact for the authenticated organization.

If a user with the provided email doesn't exist, one will be created automatically. The email must be unique within the organization's contacts.

Authorizations:

bearer

Request Body schema: application/json optional

Contact params

contact
required
object

Responses

201

Contact created

401

Unauthorized

422

Validation errors

post/api/v1/contacts

https://secure.agree.com/api/v1/contacts

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"contact": {"company": "Acme Inc",

"email": "john.doe@example.com",

"name": "John Doe",

"title": "CEO"

}

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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"

}

}`

tag/Contacts/operation/AgreeWeb.API.V1.ContactController.delete Delete contact

Deletes a contact by ID.

Authorizations:

bearer

path Parameters
id
required
string
Contact ID (UUID)

Responses

204

Contact deleted

401

Unauthorized

403

Forbidden

404

Not found

delete/api/v1/contacts/{id}

https://secure.agree.com/api/v1/contacts/{id}

Response samples

Content type

application/json

Copy

`{"error": "Invalid or missing API key"

}`

tag/Contacts/operation/AgreeWeb.API.V1.ContactController.show Get contact

Returns a single contact by ID.

Authorizations:

bearer

path Parameters
id
required
string
Contact ID (UUID)

Responses

200

Contact

401

Unauthorized

403

Forbidden

404

Not found

get/api/v1/contacts/{id}

https://secure.agree.com/api/v1/contacts/{id}

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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"

}

}`

tag/Contacts/operation/AgreeWeb.API.V1.ContactController.update (2) Update contact

Updates an existing contact.

Authorizations:

bearer

path Parameters
id
required
string
Contact ID (UUID)
Request Body schema: application/json optional

Contact params

contact
required
object

Responses

200

Contact updated

401

Unauthorized

403

Forbidden

404

Not found

422

Validation errors

patch/api/v1/contacts/{id}

https://secure.agree.com/api/v1/contacts/{id}

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"contact": {"company": "Acme Inc",

"email": "john.doe@example.com",

"name": "John Doe",

"title": "CEO"

}

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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"

}

}`

tag/Contacts/operation/AgreeWeb.API.V1.ContactController.update Update contact

Updates an existing contact.

Authorizations:

bearer

path Parameters
id
required
string
Contact ID (UUID)
Request Body schema: application/json optional

Contact params

contact
required
object

Responses

200

Contact updated

401

Unauthorized

403

Forbidden

404

Not found

422

Validation errors

put/api/v1/contacts/{id}

https://secure.agree.com/api/v1/contacts/{id}

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"contact": {"company": "Acme Inc",

"email": "john.doe@example.com",

"name": "John Doe",

"title": "CEO"

}

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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"

}

}`

tag/Webhooks Webhooks

Receive real-time notifications when events occur in your Agree account.

tag/Webhooks/Overview Overview

Webhooks 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.

Key concepts:

tag/Webhooks/Quick-Setup Quick Setup

1. Create an Endpoint

Register a URL to receive webhooks:

Response:

{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "url": "https://your-app.com/webhooks/agree",
    "events": ["invoice.paid", "invoice.failed"],
    "active": true,
    "failure_count": 0,
    "secret": "whsec_abc123xyz789...",
    "inserted_at": "2025-01-15T10:30:00Z",
    "updated_at": "2025-01-15T10:30:00Z"
  }
}

Important: Save the secret - it's only returned once at creation. You'll need it to verify webhook signatures.

2. Handle Incoming Webhooks

When an event occurs, Agree sends a POST request to your endpoint:

{
  "event": "invoice.paid",
  "payload": {
    "id": "4a755746-ba45-4226-a669-aebc7ad3719c",
    "status": "paid",
    "amount": {"amount": 15000, "currency": "USD"},
    "paid_at": "2025-01-20T14:30:00Z"
  }
}

3. Verify the Signature

Always verify webhooks came from Agree before processing them. See Verifying Webhooks below.

4. Test Your Integration

Send a test webhook to verify your endpoint works:

curl -X POST https://api.agree.com/api/v1/webhook_endpoints/550e8400-e29b-41d4-a716-446655440000/test \
  -H "Authorization: Bearer YOUR_API_KEY"

tag/Webhooks/Available-Events Available Events

Subscribe to the events your application needs:

Event Description When it fires
invoice.created Invoice was created After POST /invoices
invoice.sent Invoice emailed to customer When delivery completes
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)
invoice.paid Payment successful After payment confirmation
invoice.failed Payment attempt failed After payment rejection
invoice.canceled Invoice was canceled After DELETE /invoices
invoice.refunded Invoice payment was refunded After refund is processed
agreement.created Agreement was created After POST /agreements
agreement.sent Agreement was sent to recipients When status changes to 'sent'
agreement.signed Agreement was signed by a recipient When a recipient signs
agreement.executed Agreement was fully executed When all signers have signed
webhook.test Test event When you trigger a test

Tip: Start with invoice.paid and invoice.failed - these are the most important for payment integrations.

tag/Webhooks/Webhook-Payload Webhook Payload

Each webhook request includes:

Headers

Header Description
Content-Type application/json
X-Webhook-Signature HMAC-SHA256 signature (hex, lowercase)
X-Webhook-Timestamp Unix timestamp when sent

Body

{
  "event": "invoice.paid",
  "payload": {
    // Full invoice object with all fields
  }
}

The payload contains the complete resource object, so you have all the data you need without making additional API calls.

For 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.

tag/Webhooks/Verifying-Webhooks Verifying Webhooks

Always verify webhook signatures before processing. This ensures the request actually came from Agree and wasn't tampered with.

How Verification Works

  1. Get the raw request body (before JSON parsing)
  2. Compute HMAC-SHA256 using your endpoint's secret as the key
  3. Hex-encode the result (lowercase)
  4. Compare to the X-Webhook-Signature header using constant-time comparison

Node.js Example

const crypto = require('crypto');

function verifyWebhookSignature(rawBody, signatureHeader, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

return crypto.timingSafeEqual(
    Buffer.from(expectedSignature),
    Buffer.from(signatureHeader)
  );
}

// Express middleware example
app.post('/webhooks/agree', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-webhook-signature'];

if (!verifyWebhookSignature(req.body, signature, process.env.AGREE_WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

const event = JSON.parse(req.body);

switch (event.event) {
    case 'invoice.paid':
      // Handle successful payment
      break;
    case 'invoice.failed':
      // Handle failed payment
      break;
  }

res.status(200).send('OK');
});

Python Example

import hmac
import hashlib
from flask import Flask, request

app = Flask(__name__)

def verify_webhook_signature(raw_body, signature_header, secret):
    expected_signature = hmac.new(
        secret.encode('utf-8'),
        raw_body,
        hashlib.sha256
    ).hexdigest()

return hmac.compare_digest(expected_signature, signature_header)

@app.route('/webhooks/agree', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-Webhook-Signature')

if not verify_webhook_signature(request.data, signature, AGREE_WEBHOOK_SECRET):
        return 'Invalid signature', 401

event = request.json

if event['event'] == 'invoice.paid':
        # Handle successful payment
        pass
    elif event['event'] == 'invoice.failed':
        # Handle failed payment
        pass

return 'OK', 200

tag/Webhooks/Managing-Endpoints Managing Endpoints

List All Endpoints

curl https://api.agree.com/api/v1/webhook_endpoints \
  -H "Authorization: Bearer YOUR_API_KEY"

Update an Endpoint

Change the subscribed events or URL:

curl -X PUT https://api.agree.com/api/v1/webhook_endpoints/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_endpoint": {
      "events": ["invoice.paid", "invoice.failed", "invoice.created"]
    }
  }'

Disable an Endpoint

Set active to false to temporarily stop receiving webhooks:

curl -X PUT https://api.agree.com/api/v1/webhook_endpoints/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_endpoint": {
      "active": false
    }
  }'

Delete an Endpoint

curl -X DELETE https://api.agree.com/api/v1/webhook_endpoints/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer YOUR_API_KEY"

tag/Webhooks/Retry-Policy Retry Policy

If your endpoint returns a non-2xx status code or times out, Agree automatically retries:

Attempt Delay
1 Immediate
2 ~1 minute
3 ~5 minutes
4 ~30 minutes
5 ~2 hours

After 5 failed attempts, the webhook is marked as failed and the endpoint's failure_count is incremented.

Tip: Monitor failure_count to detect integration issues. If it keeps increasing, check your endpoint's logs.

tag/Webhooks/Best-Practices Best Practices

  1. Respond quickly - Return 200 within 5 seconds, then process asynchronously
  2. Handle duplicates - Webhooks may occasionally be sent more than once; use idempotency
  3. Verify signatures - Never skip verification in production
  4. Use HTTPS - Required in production for security
  5. Log everything - Store webhook payloads for debugging and auditing

tag/Webhooks/Fields-Reference Fields Reference

Field Type Description
id UUID Unique endpoint identifier
url string Your webhook URL (HTTPS required in production)
events array Event types this endpoint receives
active boolean Whether the endpoint is receiving webhooks
failure_count integer Consecutive failed deliveries
secret string Signing secret (only returned on creation)
inserted_at datetime When the endpoint was created
updated_at datetime When the endpoint was last modified

tag/Webhooks/operation/AgreeWeb.API.V1.WebhookEndpointController.index List webhook endpoints

Returns all webhook endpoints for the authenticated organization.

Authorizations:

bearer

Responses

200

Webhook endpoints list

401

Unauthorized

get/api/v1/webhooks

https://secure.agree.com/api/v1/webhooks

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{"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"\
}

]

}`

tag/Webhooks/operation/AgreeWeb.API.V1.WebhookEndpointController.create Create webhook endpoint

Creates a new webhook endpoint for the authenticated organization.

Important: The signing secret is only returned once in the response when the endpoint is created. Store it securely as it cannot be retrieved again.

Use the secret to verify webhook signatures by computing an HMAC-SHA256 of the request body and comparing it to the X-Agree-Signature header.

Authorizations:

bearer

Request Body schema: application/json optional

Webhook endpoint params

webhook_endpoint
required
object

Responses

201

Webhook endpoint created

401

Unauthorized

422

Validation errors

post/api/v1/webhooks

https://secure.agree.com/api/v1/webhooks

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"webhook_endpoint": {"events": ["invoice.created",

"invoice.paid",

"invoice.failed"

],

"url": "https://example.com/webhooks"

}

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"active": true,

"events": ["string"

],

"failure_count": 0,

"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",

"inserted_at": "2019-08-24T14:15:22Z",

"secret": "string",

"updated_at": "2019-08-24T14:15:22Z",

"url": "http://example.com"

}

}`

tag/Webhooks/operation/AgreeWeb.API.V1.WebhookEndpointController.delete Delete webhook endpoint

Deletes a webhook endpoint by ID.

Authorizations:

bearer

path Parameters
id
required
string
Webhook endpoint ID (UUID)

Responses

204

Webhook endpoint deleted

401

Unauthorized

404

Not found

delete/api/v1/webhooks/{id}

https://secure.agree.com/api/v1/webhooks/{id}

Response samples

Content type

application/json

Copy

`{"error": "Invalid or missing API key"

}`

tag/Webhooks/operation/AgreeWeb.API.V1.WebhookEndpointController.show Get webhook endpoint

Returns a single webhook endpoint by ID.

Authorizations:

bearer

path Parameters
id
required
string
Webhook endpoint ID (UUID)

Responses

200

Webhook endpoint

401

Unauthorized

404

Not found

get/api/v1/webhooks/{id}

https://secure.agree.com/api/v1/webhooks/{id}

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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"

}

}`

tag/Webhooks/operation/AgreeWeb.API.V1.WebhookEndpointController.update (2) Update webhook endpoint

Updates an existing webhook endpoint.

Authorizations:

bearer

path Parameters
id
required
string
Webhook endpoint ID (UUID)
Request Body schema: application/json optional

Webhook endpoint params

webhook_endpoint
required
object

Responses

200

Webhook endpoint updated

401

Unauthorized

404

Not found

422

Validation errors

patch/api/v1/webhooks/{id}

https://secure.agree.com/api/v1/webhooks/{id}

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"webhook_endpoint": {"events": ["invoice.created",

"invoice.paid",

"invoice.failed"

],

"url": "https://example.com/webhooks"

}

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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"

}

}`

tag/Webhooks/operation/AgreeWeb.API.V1.WebhookEndpointController.update Update webhook endpoint

Updates an existing webhook endpoint.

Authorizations:

bearer

path Parameters
id
required
string
Webhook endpoint ID (UUID)
Request Body schema: application/json optional

Webhook endpoint params

webhook_endpoint
required
object

Responses

200

Webhook endpoint updated

401

Unauthorized

404

Not found

422

Validation errors

put/api/v1/webhooks/{id}

https://secure.agree.com/api/v1/webhooks/{id}

Request samples

Content type

application/json

Copy Expand all Collapse all

`{"webhook_endpoint": {"events": ["invoice.created",

"invoice.paid",

"invoice.failed"

],

"url": "https://example.com/webhooks"

}

}`

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"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"

}

}`

tag/Webhooks/operation/AgreeWeb.API.V1.WebhookEndpointController.test Send test webhook

Sends a test webhook payload to all endpoints subscribed to the webhook.test event.

This is useful for verifying your webhook endpoint is properly configured to receive events.

The test payload contains:

{
  "test": true,
  "message": "This is a test webhook from Agree",
  "timestamp": "2024-01-15T10:30:00Z"
}
Authorizations:

bearer

Responses

202

Test webhooks queued

401

Unauthorized

404

No endpoints subscribed to webhook.test

post/api/v1/webhooks/test

https://secure.agree.com/api/v1/webhooks/test

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": {"failed": 0,

"job_ids": [123,

124

],

"message": "Test webhooks have been queued and will be sent shortly",

"successful": 2,

"total": 2

}

}`

tag/Reports Reports

Read-only analytics for revenue, cashflow, and accounts receivable recovery.

tag/Reports/Overview Overview

Report endpoints mirror the in-app Reports dashboards (/reports/revenue, /reports/cashflow, /reports/recovery). All metrics are scoped to the authenticated API key's organization.

Amounts 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").

tag/Reports/Revenue Revenue

Endpoint Description
GET /reports/revenue/stats ARR, MRR, growth, NRR, top-5 concentration
GET /reports/revenue/chart Monthly MRR/ARR history and forecast
GET /reports/revenue/customers Customers ranked by paid revenue
GET /reports/revenue/customers_by_mrr Customers ranked by subscription MRR

tag/Reports/Cashflow Cashflow

Endpoint Description
GET /reports/cashflow/stats Cash collected MTD, outstanding total, DSO, fail rate
GET /reports/cashflow/chart Monthly cash collected history and forecast
GET /reports/cashflow/outstanding_invoices Outstanding invoice list
GET /reports/cashflow/forecast Expected cash in next 30/60/90 days

tag/Reports/Recovery Recovery

Endpoint Description
GET /reports/recovery/aging/chart Aging buckets by month
GET /reports/recovery/aging/invoices Invoices grouped by aging bucket
GET /reports/recovery/aging/trend Overdue totals and average days overdue
GET /reports/recovery/leakage/stats Counts/amounts by invoice stage
GET /reports/recovery/leakage/waterfall Stage waterfall for leakage analysis
GET /reports/recovery/leakage/stalled_invoices Stalled invoices
GET /reports/recovery/leakage/stage_durations Average days between stages

tag/Reports/operation/AgreeWeb.API.V1.ReportController.revenue_stats Revenue statistics

Returns ARR, MRR, MRR growth rate, net revenue retention (NRR), and top-5 customer concentration.

Authorizations:

bearer

Responses

200

Revenue statistics

401

Unauthorized

get/api/v1/reports/revenue/stats

https://secure.agree.com/api/v1/reports/revenue/stats

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": { }

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.recovery_aging_trend Aging trend

Returns total overdue amount and average days overdue per month.

Authorizations:

bearer

Responses

200

Aging trend

401

Unauthorized

get/api/v1/reports/recovery/aging/trend

https://secure.agree.com/api/v1/reports/recovery/aging/trend

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{ }

]

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.revenue_chart Revenue chart data

Returns historical and forecast MRR/ARR by month.

Authorizations:

bearer

Responses

200

Revenue chart

401

Unauthorized

get/api/v1/reports/revenue/chart

https://secure.agree.com/api/v1/reports/revenue/chart

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{ }

]

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.recovery_leakage_stage_durations Stage duration statistics

Returns average days between invoice stages for current vs previous six-month periods.

Authorizations:

bearer

Responses

200

Stage durations

401

Unauthorized

get/api/v1/reports/recovery/leakage/stage_durations

https://secure.agree.com/api/v1/reports/recovery/leakage/stage\_durations

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": { }

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.recovery_leakage_stats Leakage statistics

Returns invoice counts and amounts by stage (drafted, outstanding, overdue, paid).

Authorizations:

bearer

Responses

200

Leakage statistics

401

Unauthorized

get/api/v1/reports/recovery/leakage/stats

https://secure.agree.com/api/v1/reports/recovery/leakage/stats

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": { }

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.recovery_leakage_waterfall Leakage waterfall

Returns waterfall stages for invoice progression (drafted → outstanding → overdue → paid).

Authorizations:

bearer

Responses

200

Leakage waterfall

401

Unauthorized

get/api/v1/reports/recovery/leakage/waterfall

https://secure.agree.com/api/v1/reports/recovery/leakage/waterfall

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{ }

]

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.cashflow_chart Cashflow chart data

Returns historical and forecast cash collected by month.

Authorizations:

bearer

Responses

200

Cashflow chart

401

Unauthorized

get/api/v1/reports/cashflow/chart

https://secure.agree.com/api/v1/reports/cashflow/chart

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{ }

]

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.recovery_leakage_stalled_invoices Stalled invoices

Lists invoices that have been stalled in a workflow stage.

Authorizations:

bearer

Responses

200

Stalled invoices

401

Unauthorized

get/api/v1/reports/recovery/leakage/stalled_invoices

https://secure.agree.com/api/v1/reports/recovery/leakage/stalled\_invoices

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{ }

]

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.cashflow_forecast Cash forecast

Returns projected cash from outstanding invoices due in the next 30, 60, and 90 days.

Authorizations:

bearer

Responses

200

Cash forecast

401

Unauthorized

get/api/v1/reports/cashflow/forecast

https://secure.agree.com/api/v1/reports/cashflow/forecast

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": { }

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.revenue_customers_by_mrr Customers by MRR

Lists customers ranked by current monthly recurring revenue from active subscriptions.

Authorizations:

bearer

Responses

200

Customers by MRR

401

Unauthorized

get/api/v1/reports/revenue/customers_by_mrr

https://secure.agree.com/api/v1/reports/revenue/customers\_by\_mrr

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{ }

]

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.cashflow_outstanding_invoices Outstanding invoices

Lists outstanding (sent or due) invoices with customer and aging details.

Authorizations:

bearer

Responses

200

Outstanding invoices

401

Unauthorized

get/api/v1/reports/cashflow/outstanding_invoices

https://secure.agree.com/api/v1/reports/cashflow/outstanding\_invoices

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{ }

]

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.cashflow_stats Cashflow statistics

Returns cash collected month-to-date, outstanding invoice total, days sales outstanding (DSO), and payment failure rate.

Authorizations:

bearer

Responses

200

Cashflow statistics

401

Unauthorized

get/api/v1/reports/cashflow/stats

https://secure.agree.com/api/v1/reports/cashflow/stats

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": { }

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.revenue_customers Customers by revenue

Lists customers ranked by total paid invoice revenue.

Authorizations:

bearer

Responses

200

Customers by revenue

401

Unauthorized

get/api/v1/reports/revenue/customers

https://secure.agree.com/api/v1/reports/revenue/customers

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{ }

]

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.recovery_aging_chart Aging chart data

Returns overdue invoice amounts by aging bucket for the past six months.

Authorizations:

bearer

Responses

200

Aging chart

401

Unauthorized

get/api/v1/reports/recovery/aging/chart

https://secure.agree.com/api/v1/reports/recovery/aging/chart

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": [{ }

]

}`

tag/Reports/operation/AgreeWeb.API.V1.ReportController.recovery_aging_invoices Invoices by aging bucket

Lists outstanding invoices grouped by aging bucket (current, 1-30, 31-60, 61-90, 90+ days).

Authorizations:

bearer

Responses

200

Invoices by aging

401

Unauthorized

get/api/v1/reports/recovery/aging/invoices

https://secure.agree.com/api/v1/reports/recovery/aging/invoices

Response samples

Content type

application/json

Copy Expand all Collapse all

`{"data": { }

}`