Skip to main content

Base URL

All API requests should be made to:
https://api.tomorro.com

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)
ExternalCompanyCounterparty
AttributeField
ContractMemberMember (on contract)
AttributeDefinitionField options (inline)

Response Format

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",
    ...
  }
}

Pagination

List endpoints use cursor-based pagination for efficient traversal of large datasets.

Parameters

ParameterTypeDefaultDescription
limitinteger20Number 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

FormatDescriptionExample
field=valueEquality (default)status=active
field=in:value1,value2In list (OR condition)status=in:draft,negotiating
field=gte:valueGreater than or equalstartAt=gte:2024-01-01
field=lte:valueLess than or equalendAt=lte:2024-12-31
field=ne:valueNot equalsstatus=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

CodeDescription
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 Response Format

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

CodeDescription
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