> ## Documentation Index
> Fetch the complete documentation index at: https://alpha.developer.tomorro.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API Reference

> Complete reference for the Tomorro Public API

## Base URL

All API requests should be made to:

```
https://api.tomorro.com/v2
```

## Authentication

The Tomorro API uses API keys for authentication. Include your API key in the `x-api-key` header with every request.

```bash theme={null}
curl -X GET "https://api.tomorro.com/v2/contracts" \
  -H "x-api-key: your-api-key"
```

<Card title="Get your API key" icon="key" href="https://app.tomorro.com/settings/integrations?integration=api-key">
  Generate an API key from your organization settings.
</Card>

<Warning>
  Keep your API key secure, do not share it in publicly accessible areas such as GitHub, client-side code, or public
  repositories.
</Warning>

## Terminology

| API name        | Tomorro name   |
| --------------- | -------------- |
| `type`          | Contract type  |
| `template`      | Template       |
| `contract`      | Contract       |
| `smart-field`   | Smart field    |
| `counterparty`  | Counterparty   |
| `member`        | Member         |
| `clauses`       | Clauses        |
| `custom object` | Dynamic tables |

## Response Format

All responses are returned in JSON format. Successful responses wrap the data in a `data` field:

```json theme={null}
{
  "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

| Parameter | Type    | Default | Description                      |
| --------- | ------- | ------- | -------------------------------- |
| `limit`   | integer | 20      | Number of items per page (1-100) |
| `after`   | string  | -       | Cursor for forward pagination    |
| `before`  | string  | -       | Cursor for backward pagination   |

### Response

Paginated responses include a `pagination` object:

```json theme={null}
{
  "data": [...],
  "pagination": {
    "hasNext": true,
    "hasPrevious": false,
    "next": "eyJpZCI6IjEyMyJ9",
    "previous": null
  }
}
```

### Example: Fetching pages

```bash theme={null}
# First page
curl "https://api.tomorro.com/v2/contracts?limit=20" \
  -H "x-api-key: your-api-key"

# Next page (using cursor from previous response)
curl "https://api.tomorro.com/v2/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.

```bash theme={null}
# 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=value`            | Equality (default)     | `status=active`               |
| `field=in:value1,value2` | In list (OR condition) | `status=in:draft,negotiating` |
| `field=gte:value`        | Greater than or equal  | `startAt=gte:2024-01-01`      |
| `field=lte:value`        | Less than or equal     | `endAt=lte:2024-12-31`        |
| `field=ne:value`         | Not equals             | `status=ne:canceled`          |

On `GET /contracts`, `contractTypeId` applies to **one** contract type UUID per request. Using `in:` with several UUIDs (for example `contractTypeId=in:uuid1,uuid2`) is not supported for multiple types; make one request per contract type and merge the results client-side.

### Examples

```bash theme={null}
# 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                               |
| ----- | ----------------------------------------- |
| `200` | Success                                   |
| `201` | Resource created                          |
| `204` | Success (no content)                      |
| `400` | Bad request - Invalid parameters          |
| `401` | Unauthorized - Invalid or missing API key |
| `404` | Not found - Resource doesn't exist        |
| `409` | Conflict - Resource state conflict        |
| `429` | Too many requests - Rate limit exceeded   |
| `500` | Internal server error                     |

### Error Response Format

```json theme={null}
{
  "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_FOUND`        | Contract does not exist               |
| `COUNTERPARTY_NOT_FOUND`    | Counterparty does not exist           |
| `TEMPLATE_NOT_FOUND`        | Template does not exist               |
| `MEMBER_NOT_FOUND`          | Member does not exist                 |
| `FIELD_NOT_FOUND`           | Unknown field name or ID              |
| `FIELD_OPTION_NOT_FOUND`    | Unknown option for select field       |
| `INVALID_STATUS_TRANSITION` | Cannot 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.

<Tip>Implement exponential backoff in your client to handle rate limiting gracefully.</Tip>

## 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):**

```bash theme={null}
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):**

```bash theme={null}
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):**

```bash theme={null}
# 1. Create contract with document
POST /contracts

# 2. Send for signature
POST /contracts/:id/signatures/send
```
