Base URL
All API requests should be made to:
Authentication
The Tomorro API uses API keys for authentication. Include your API key in the x-api-key header with every request.
curl -X GET "https://api.tomorro.com/contracts" \
-H "x-api-key: your-api-key"
Get your API key Generate an API key from your organization settings.
Keep your API key secure, do not share it in publicly accessible areas such as GitHub, client-side code, or public
repositories.
Available Resources
The Tomorro API provides access to the following resources:
Terminology
If you’re migrating from the previous GraphQL API, note these terminology changes:
Previous API (GraphQL) Current API (REST) ExternalCompanyCounterpartyAttributeFieldContractMemberMember (on contract) AttributeDefinitionField options (inline)
All responses are returned in JSON format. Successful responses wrap the data in a data field:
{
"data" : {
"id" : "ctr_550e8400-e29b-41d4-a716-446655440000" ,
"name" : "Service Agreement" ,
"status" : "draft" ,
...
}
}
List endpoints use cursor-based pagination for efficient traversal of large datasets.
Parameters
Parameter Type Default Description limitinteger 20 Number of items per page (1-100) afterstring - Cursor for forward pagination beforestring - Cursor for backward pagination
Response
Paginated responses include a pagination object:
{
"data" : [ ... ],
"pagination" : {
"hasNext" : true ,
"hasPrevious" : false ,
"next" : "eyJpZCI6IjEyMyJ9" ,
"previous" : null
}
}
Example: Fetching pages
# First page
curl "https://api.tomorro.com/contracts?limit=20" \
-H "x-api-key: your-api-key"
# Next page (using cursor from previous response)
curl "https://api.tomorro.com/contracts?limit=20&after=eyJpZCI6IjEyMyJ9" \
-H "x-api-key: your-api-key"
Sorting
Use the sort parameter to order results. Prefix with - for descending order.
# Sort by creation date (newest first)
GET /contracts?sort=-createdAt
# Sort by name ascending
GET /contracts?sort=name
Filtering
The API uses a simplified filtering format. Use query parameters directly.
Filter Operators
Format Description Example field=valueEquality (default) status=activefield=in:value1,value2In list (OR condition) status=in:draft,negotiatingfield=gte:valueGreater than or equal startAt=gte:2024-01-01field=lte:valueLess than or equal endAt=lte:2024-12-31field=ne:valueNot equals status=ne:canceled
Examples
# Simple equality
GET /contracts?status=signed
# Filter multiple values (OR condition)
GET /contracts?status=in:draft,negotiating
# Date range
GET /contracts?startAt=gte:2024-01-01 & endAt = lte:2024-12-31
# Multiple filters (AND condition)
GET /contracts?status=signed & counterpartyId = cp_123
# Search by name
GET /counterparties?name=Acme
Error Handling
The API uses standard HTTP status codes to indicate success or failure.
Status Codes
Code Description 200Success 201Resource created 204Success (no content) 400Bad request - Invalid parameters 401Unauthorized - Invalid or missing API key 404Not found - Resource doesn’t exist 409Conflict - Resource state conflict 429Too many requests - Rate limit exceeded 500Internal server error
{
"error" : {
"statusCode" : "NOT_FOUND" ,
"errorId" : "FIELD_NOT_FOUND" ,
"message" : "Field 'InvalidField' not found" ,
"details" : {
"field" : "InvalidField" ,
"providedValue" : "some value" ,
"availableOptions" : [ "Industry" , "Company Size" , "Tags" ]
}
}
}
Common Error Codes
Code Description CONTRACT_NOT_FOUNDContract does not exist COUNTERPARTY_NOT_FOUNDCounterparty does not exist TEMPLATE_NOT_FOUNDTemplate does not exist MEMBER_NOT_FOUNDMember does not exist FIELD_NOT_FOUNDUnknown field name or ID FIELD_OPTION_NOT_FOUNDUnknown option for select field INVALID_STATUS_TRANSITIONCannot transition to requested status
Rate Limiting
The API implements rate limiting to ensure fair usage. If you exceed the rate limit, you’ll receive a 429 Too Many Requests response.
Implement exponential backoff in your client to handle rate limiting gracefully.
Simplified Workflows
The v2 API significantly reduces the number of API calls needed for common operations.
Creating a Contract (Before vs After)
Before (GraphQL - 2+ calls):
1. createContract({ name, externalCompany, typeId })
2. createDocument({ contractId, templateId })
After (REST - 1 call):
POST /contracts
{
"templateId" : "tpl_...",
"counterpartyId" : "cp_...",
"fields" : { "Industry": "Technology" }
}
Updating a Field (Before vs After)
Before (GraphQL - 3+ calls):
1. Query attributeDefinitions → get ID by name
2. Query attributeDefinition → get option ID
3. updateExternalCompanyAttributes({ attributeDefinitionId, value: optionId })
After (REST - 1 call):
PATCH /counterparties/:id
{ "fields" : { "Industry": "Technology" } }
Sending for Signature (Before vs After)
Before (GraphQL - 4-6 calls):
1. createContract
2. createDocument
3. assignSignatoryUser (per signatory)
4. prepareDocumentForSignature
5. acceptForSignature
After (REST - 2 calls):
# 1. Create contract with document
POST /contracts
# 2. Send for signature
POST /contracts/:id/signatures/send