GET /api/v1/contracts
Returns the workspace's contracts, soonest-ending first, so the default page is also the renewal queue. Contracts with no term yet sort last: nothing without an end date can be expiring. Requires the read:contracts scope (or * for full access).
Request
GET /api/v1/contracts HTTP/1.1
Host: agentlyleads.com
Authorization: Bearer alk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxQuery parameters
| Parameter | Description |
|---|---|
status | DRAFT, ACTIVE, EXPIRED or CANCELLED |
companyId | Only contracts held by this company |
dealId | Only the contract that came out of this deal |
ownerId | Only contracts owned by this workspace user |
expiringWithinDays | Only terms ending between now and this many days out (1 to 3650) |
updatedSince | ISO 8601 datetime. Only contracts updated at or after this moment (useful for incremental sync) |
limit | Page size, 1–1000 (default 100) |
cursor | The nextCursor value from the previous page |
All parameters are optional and can be combined. Filters that match nothing return an empty list, not a 404.
The renewal queue for the next quarter:
GET /api/v1/contracts?status=ACTIVE&expiringWithinDays=90 HTTP/1.1
Authorization: Bearer alk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxexpiringWithinDays is forward-looking on purpose. A term that already ran out is an EXPIRED status, which is a different problem from a renewal to prepare for, so those rows are excluded rather than mixed in.
Everything signed with one account:
GET /api/v1/contracts?companyId=cmq2comp0alk34567890 HTTP/1.1
Authorization: Bearer alk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxResponse — 200
{
"contracts": [
{
"id": "cmq2cont0alk34567890",
"number": 12,
"title": "Acme master agreement",
"status": "ACTIVE",
"value": 12500.5,
"startsAt": "2026-01-01T00:00:00.000Z",
"endsAt": "2026-12-31T00:00:00.000Z",
"autoRenew": true,
"renewalNotifiedAt": null,
"notes": null,
"company": { "id": "cmq2comp0alk34567890", "name": "Acme Corp" },
"deal": { "id": "cmq2deal0alk34567890", "name": "Acme 2026 renewal" },
"owner": { "id": "cmq9user0alk34567890", "name": "Kevin Rep" },
"createdAt": "2025-12-14T12:00:00.000Z",
"updatedAt": "2026-01-02T09:31:00.000Z"
}
],
"nextCursor": null
}Ordering
Rows come back by endsAt ascending, then newest-created. That puts the next thing to renew at the top of page one and leaves contracts with no term at the end of the list.
Pagination
Responses are cursor-paginated. When more rows exist beyond the requested limit, nextCursor holds the ID of the last returned record. Pass it back as cursor to fetch the next page. On the last page, nextCursor is null.
Writing contracts
There is no write endpoint. Contracts are created in the app or through the AI connector, where create_contract can fill title, company, owner and value straight from a won deal. The reason is the renewal alert: an integration that could move endsAt would move the warning with it, silently, and nobody would find out until the term had already rolled over.