# Setup

Learn how to get your API key and how to send requests to the platform.

### Obtaining an API key

All accounts can request an API key by visiting the ["API Settings" section of your account](https://annolab.ai/settings/api-keys)

![How to get to settings page from dashboard](https://2539662686-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MR10rNw-iCGaHMIJEII%2F-MRHAw8KFcOVNw1MAFBg%2F-MRHD5kDiJLuUx3Nfco1%2Fapi_settings_get_there.png?alt=media\&token=c024c831-c06c-45c6-81a2-d39607527d56)

On the api-key page you can generate a new key with READ, WRITE, or both privileges.

![](https://2539662686-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MR10rNw-iCGaHMIJEII%2F-MRHAw8KFcOVNw1MAFBg%2F-MRHDnpdpPGrMZ6Grauu%2Fapi_settings_page_generate_new.png?alt=media\&token=6813c2e7-7aa4-4bb0-9a93-688f329bb4a7)

If you generate a new key, please remember to copy the key to a safe location. This will be your only opportunity to copy the key and if you lose it you will need to generate a new one.

### Sending an API Request

Anno Lab accepts generic http requests that can be made from any programming language. Throughout this documentation site we have provided code samples to see concrete usage in action. Authorization for the request must be included in the header, specifically in the `Authorization` section with the pattern shown below.

For particular routes and depending on how you send the request, you also may need to manually set the `Content-Type` header to have the value `application/json`. Many packages will automatically set this header (such as python `requests` used throughout this tutorial when you pass data using the json argument).

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

project = {
  'name': 'New NER Project'
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/project/create'

response = requests.post(url, headers=headers, json=project)

print(response.json())
```

{% endtab %}
{% endtabs %}


# AnnoLab SDK

Python SDK to interact with AnnoLab

AnnoLab has a python SDK to improve the experience of performing certain AnnoLab actions. We highly recommend using this SDK for things like uploading files, as it greatly speeds uploads and simplifies the experience.

[Pypi link](https://pypi.org/project/annolab/)

### Installing via pip

```
pip install annolab
```

### Using the sdk

To get started, ensure you have an annolab account at <https://app.annolab.ai/signup> and have created an API Key. Instructions for creating an API Key may be found at <https://docs.annolab.ai/>.

1. Create an instance of the SDK passing your api\_key.

```python
from annolab import AnnoLab
lab = AnnoLab(api_key='YOUR_API_KEY')
```

Alternatively, set the `ANNOLAB_API_KEY` environment variable and construct the client with no arguments: `lab = AnnoLab()`.

### Using the sdk to upload pdf source

For pdf uploads to work, the user associated with your api key must have permissions to edit sources on your project. Permissions can be modified by admins on the appropriate project details page found under <https://app.annolab.ai/projects>

Obtain the project

```python
project = lab.find_project('My Project')
```

Upload from a local file system to a specific directory in your project. OCR the pdf using annolab OCR

```python
project.create_pdf_source(
    file='/path/to/file', 
    name='custom_name.pdf', 
    directory='Uploads',
    ocr=True,
    ocrProvider='textract_plus', # textract_plus is the same as "Annolab" OCR
)
```

### Using the sdk to upload pdf source and trigger an AI workflow

For pdf uploads invoking workflows to work, the user associated with your api key must have permissions to edit sources on your project and must have the ability to "run" machine learning models on your project.

Obtain the project

```python
project = lab.find_project('My Project')
```

Upload from a local file system to a specific directory in your project. Use the OCR provider and suite of ML models specified by a workflow

```python
project.create_pdf_source(
    file='/path/to/file', 
    name='custom_name.pdf', 
    directory='Uploads',
    workflow='aircraft_title', 
)
```


# Projects

Projects are the highest level abstraction in the Annotation Lab platform. They contain directories, source files, schemas, and annotation layers. They are also the scope at which work can be shared.

## Create Project

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/project/create`

Create a new project within your group

#### Headers

| Name          | Type   | Description                                                                                                                                                             |
| ------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Creating a project requires a key with "Write" permissions.<br><br><code>{"Authorization": "Api-key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name | Type   | Description                                                                                                                  |
| ---- | ------ | ---------------------------------------------------------------------------------------------------------------------------- |
| name | string | <p>Name of the project you wish to create. Must be unique for your group<br><br><code>{"name": "New NER Project"}</code></p> |

{% tabs %}
{% tab title="201 Project was successfully created" %}

```
{
    "name": "New NER Project",
    "id": 22,
    "groupId": 14
}
```

{% endtab %}

{% tab title="400 Project creation failed" %}

```
{
    "message": "Information about why creation failed"
}
```

{% endtab %}
{% endtabs %}

Examples of how to make a project create request

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

project = {
  'name': 'New NER Project'
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/project/create'

response = requests.post(url, headers=headers, json=project)

print(response.json())
```

{% endtab %}
{% endtabs %}

## Get Project

<mark style="color:blue;">`GET`</mark> `https://api.annolab.ai/v1/project/{identifier}`

Return details of a project given an identifier

#### Path Parameters

| Name       | Type   | Description                                                                                                                                                                                          |
| ---------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| identifier | string | <p>Either the name or the id of a project<br><br><code>url = "<https://api.annolab.ai/v1/project/New%20NER%20Project>"</code><br><br><code>url = "<https://api.annolab.ai/v1/project/12>"</code></p> |

#### Headers

| Name          | Type   | Description                                                                                                                                                           |
| ------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Getting a project requires an key with "Read" permissions<br><br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

{% tabs %}
{% tab title="200 Project successfully retrieved" %}

```
{
    "name": "New NER Project",
    "id": 22,
    "groupId": 14
}
```

{% endtab %}

{% tab title="404 Could not find a project matching the information" %}

```
{}
```

{% endtab %}
{% endtabs %}


# Directories

Directories are containers for Source Files that can be used to help organize the contents of a project

## Create Directory

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/directory/create`

Create a new directory within the project

#### Headers

| Name          | Type   | Description                                                                                                                                                           |
| ------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Creating a directory requires a key with "Write" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name              | Type   | Description                                                                                                       |
| ----------------- | ------ | ----------------------------------------------------------------------------------------------------------------- |
| projectIdentifier | string | Identifier of the project that the directory will be created within. Either id or the unique name of the project. |
| name              | string | Name of the directory you wish to create                                                                          |

{% tabs %}
{% tab title="201 The directory was created" %}

```
{
    "name": "Wikipedia Subset",
    "id": 12,
    "projectname": "New NER Project",
    "projectId": 22
}
```

{% endtab %}

{% tab title="400 The directory could not be created" %}

```
{
    "message": "explanation for why directory could not be created"
}
```

{% endtab %}
{% endtabs %}

Example of how to make the request to create a directory

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

directory = {
  'projectIdentifier': 'New NER Project',
  'name': 'Wikipedia Subset'
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/directory/create'

response = requests.post(url, headers=headers, json=directory)

print(response.json())
```

{% endtab %}
{% endtabs %}


# Source Files

Source files are files that contain things you want to annotate or run models on. Currently we support PDF and .txt file formats

## Retrieve source information

<mark style="color:blue;">`GET`</mark> `https://api.annolab.ai/v1/source/{source_id}`

Returns basic source information, including a signed URL to download the original file and its tags.

#### Query Parameters

| Name                                         | Type | Description      |
| -------------------------------------------- | ---- | ---------------- |
| source\_id<mark style="color:red;">\*</mark> | Int  | Id of the source |

#### Headers

| Name                                            | Type   | Description                                                                              |
| ----------------------------------------------- | ------ | ---------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>Your API key<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

{% tabs %}
{% tab title="200: OK " %}

{% endtab %}
{% endtabs %}

```json
// Example Response
{
    "id": 2,
    "projectName": "My Project",
    "projectId": 1,
    "directoryName": "Uploads",
    "directoryId": 1,
    "name": "REGISTRATION.pdf",
    "sourceName": "REGISTRATION.pdf",
    "type": "pdf",
    "text": "Example PDF Text",
    "url": "https://download-example-url.pdf",
    "createdAt": "2023-05-22T20:32:02.633Z",
    "tags": [
        {
            "domainEntityId": 1,
            "typeName": "Airframe Inventory",
            "attributes": [
                {
                    "name": "Make",
                    "value": "CESSNA"
                },
                {
                    "name": "Model",
                    "value": "421C"
                },
                {
                    "name": "Serial Number",
                    "value": "421C-5837"
                }
            ],
            "createdBy": {
                "id": 58473,
                "email": "testuser@gmail.com",
                "username": "testuser"
            }
        }
    ]
}
```

## Upload a PDF

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/source/upload-pdf`

Upload a PDF and specify an OCR method to apply. (optional) invoke a workflow of AI models

#### Headers

| Name                                            | Type   | Description                                                                                                                                                           |
| ----------------------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>Where you put your api key. Creating a directory requires a key with "Write" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                                                | Type            | Description                                                                                                                                        |
| --------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| projectIdentifier<mark style="color:red;">\*</mark> | string\|number  | Either id of the project or name of the project where file will reside                                                                             |
| directoryIdentifier                                 | string          | name of the directory where the file will reside                                                                                                   |
| sourceIdentifier<mark style="color:red;">\*</mark>  | string          | Name of the source that will be created                                                                                                            |
| ocrProvider                                         | string          | Only used if processMode is set to OCR. Valid values are "textract", "textract\_plus", and "gcv". "textract\_plus" recommended for highest quality |
| preprocessor                                        | string          | Valid options are "faa" and None.                                                                                                                  |
| groupName<mark style="color:red;">\*</mark>         | string          | Name of the group that owns the project                                                                                                            |
| tags                                                | CanonicalTag\[] | Array of [CanonicalTag](/annotations-and-relations/canonical-tags#canonicaltag-object) objects                                                     |
| workflow                                            | string          | Workflow (aka package of AI models) that will be invoked immediately after upload. Recommend "FAA\_CD\_WITH\_VLM" or "FAA\_CD\_WITH\_TAGGING"      |
| processMode                                         | string          | Use "OCR" if the pdf is not already text enriched. Use "EXTRACT" if pdf already has text embedded                                                  |

{% tabs %}
{% tab title="Python" %}

```python
import os
import json
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

url_base = 'https://api.annolab.ai'

input_pdf = '/Users/grantdelozier/devel/ocr-these3/TEST-REGISTRATION.PDF'

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = url_base+'/v1/source/create-pdf'

requestBody = {
  'groupName': 'AnnoLab',
  'projectIdentifier': 'title-demo',
  'directoryIdentifier': 'testing',
  'sourceIdentifier': 'TEST-REGISTRATION.PDF',
  'preprocessor': 'faa',
  'processMode': 'OCR',
  'ocrProvider': 'textract_plus',
  'workflow': 'FAA_CD'
}

fileToUpload = {
  'file': ('TEST-REGISTRATION.PDF', open(input_pdf, 'rb'), 'application/pdf')
}

url = url_base+'/v1/source/upload-pdf'

response = requests.post(url, headers=headers, data=requestBody, files=fileToUpload)
print(response.json())

```

{% endtab %}
{% endtabs %}

## Create Source Text

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/source/create-text`

Create a new text file source within a directory.

#### Headers

| Name                                            | Type   | Description                                                                                                                                                           |
| ----------------------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | string | <p>Where you put your api key. Creating a directory requires a key with "Write" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                | Type   | Description                                                                                      |
| ------------------- | ------ | ------------------------------------------------------------------------------------------------ |
| projectIdentifier   | string | Identifier for the project that will contain the source file. Either the id or the unique name   |
| directoryIdentifier | string | Identifier for the directory that will contain the source file. Either the id or the unique name |
| sourceName          | string | Name of the file you wish to create                                                              |
| text                | string | Text that exists within the file                                                                 |

{% tabs %}
{% tab title="201 Source text was successfully created" %}

```
{
    "sourceName": "athens.txt,
    "directoryName": "Wikipedia Subset",
    "directoryId": 12,
    "projectName": "New NER Project",
    "projectId": 22,
    "id": 145
}
```

{% endtab %}
{% endtabs %}

This code shows how to create a new text file source

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

source = {
  'projectIdentifier': 'New NER Project',
  'directoryIdentifier': 'Wikipedia Subset',
  'sourceName': 'athens.txt'
  'text': 'Athens (Greek: Αθήνα, Athína), is the capital city of Greece with a metropolitan population of 3.7 million inhabitants.'
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/source/create-text'

response = requests.post(url, headers=headers, json=source)

print(response.json())
```

{% endtab %}
{% endtabs %}


# Upload Sources

Upload PDF source files using a three-step process: initialize an upload, PUT the file to a signed URL, then confirm creation and start processing.

PDF uploads use a three-step process. This is the recommended way to add PDFs through the API.

1. **Initialize** the upload with `POST /v1/source/init-pdf`. This creates a pending source and returns a signed `uploadUrl`.
2. **Upload** the PDF with `PUT` to that `uploadUrl`. Do not send your API key on this request.
3. **Confirm** the upload with `POST /v1/source/create-pdf`. This starts OCR and, optionally, a workflow of AI models.

Complete all three steps before the pending source `expiresAt` time. Creating sources requires an API key with "Write" permissions.

## 1. Initialize PDF upload

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/source/init-pdf`

Create a pending PDF source and receive a signed URL to upload the file.

#### Headers

| Name          | Type   | Description                                                                                                                                                             |
| ------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Initializing an upload requires a key with "Write" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                | Type            | Description                                                                                                                                                              |
| ------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| projectIdentifier   | string\|integer | Id or unique name of the project that will contain the source                                                                                                            |
| sourceName          | string          | Name of the source to create                                                                                                                                             |
| directoryIdentifier | string\|integer | Optional. Id or name of the directory. If omitted, the project's default Uploads directory is used. If a name is passed and the directory does not exist, it is created. |
| groupName           | string          | Optional. Group that owns the project. When provided, the project is resolved as that group's project named or identified by `projectIdentifier`.                        |
| tags                | CanonicalTag\[] | Optional. Array of [CanonicalTag](/annotations-and-relations/canonical-tags#canonicaltag-object) objects to attach to the source                                         |
| metadata            | object          | Optional. Key-value metadata to store with the source                                                                                                                    |
| abstractId          | integer         | Optional. Abstract to associate with this source                                                                                                                         |

{% tabs %}
{% tab title="201 The pending source was created" %}

```json
{
    "projectName": "Example Land Project",
    "projectId": 22,
    "directoryName": "Uploads",
    "directoryId": 12,
    "sourceName": "deed.pdf",
    "uploadUrl": "https://s3.amazonaws.com/example-bucket/path/to/deed.pdf?X-Amz-Signature=...",
    "expiresAt": "2026-08-20T19:00:00.000Z",
    "abstractId": 1842,
    "pendingSource": {
        "pendingSourceId": 501,
        "name": "deed.pdf",
        "sourceType": "pdf",
        "createdAt": "2026-08-20T18:00:00.000Z",
        "expiresAt": "2026-08-20T19:00:00.000Z",
        "confirmedAt": "2026-08-20T19:00:00.000Z",
        "metadata": null,
        "status": null,
        "finalSourceId": null
    }
}
```

{% endtab %}

{% tab title="400 The pending source could not be created" %}

```json
{
    "message": "Project Example Land Project does not exist"
}
```

{% endtab %}
{% endtabs %}

Save `uploadUrl` for step 2 and `pendingSource.pendingSourceId` (or `sourceName`) for step 3.

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

init_body = {
  'projectIdentifier': 'Example Land Project',
  'directoryIdentifier': 'Uploads',
  'sourceName': 'deed.pdf'
}

init_response = requests.post(
  'https://api.annolab.ai/v1/source/init-pdf',
  headers=headers,
  json=init_body
)

init_json = init_response.json()
upload_url = init_json['uploadUrl']
source_name = init_json['sourceName']
```

{% endtab %}
{% endtabs %}

## 2. Upload the PDF

<mark style="color:orange;">`PUT`</mark> `{uploadUrl}`

Upload the PDF bytes to the signed URL returned by `init-pdf`. This request goes to object storage, not to `api.annolab.ai`.

Do not include the `Authorization` API key header. Send the raw file contents as the request body.

{% tabs %}
{% tab title="Python" %}

```python
pdf_path = '/path/to/deed.pdf'

with open(pdf_path, 'rb') as pdf_file:
  upload_response = requests.put(upload_url, data=pdf_file)

upload_response.raise_for_status()
```

{% endtab %}
{% endtabs %}

A successful upload typically returns `200`. After the file is in place, confirm the source in step 3.

## 3. Create the PDF source

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/source/create-pdf`

Confirm that the PDF has been uploaded and start processing. This creates the source from the pending upload and can run OCR and a workflow of AI models.

You must have permission to create sources in the project. `sourceIdentifier` must match the pending source from step 1, either by name (`sourceName`) or by `pendingSourceId`.

#### Headers

| Name          | Type   | Description                                                                                                                                                        |
| ------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Authorization | string | <p>Where you put your api key. Creating a source requires a key with "Write" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                | Type            | Description                                                                                                                                                              |
| ------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| projectIdentifier   | string\|integer | Id or unique name of the project that contains the pending source                                                                                                        |
| sourceIdentifier    | string\|integer | Pending source name from `init-pdf`, or the `pendingSourceId`                                                                                                            |
| directoryIdentifier | string\|integer | Optional. Id or name of the directory used in `init-pdf`. If omitted, the project's default Uploads directory is used.                                                   |
| groupName           | string          | Optional. Group that owns the project. When provided, the project is resolved as that group's project named or identified by `projectIdentifier`.                        |
| ocr                 | boolean         | Optional. Defaults to running OCR. Set to `false` to extract existing PDF text instead of OCR.                                                                           |
| ocrProvider         | string          | Optional. OCR engine when OCR is enabled. One of `textract`, `textract_plus`, or `gcv`. `textract_plus` is recommended for highest quality.                              |
| preprocessor        | string          | Optional. Document preprocessor. One of `faa` or `none`.                                                                                                                 |
| workflow            | string          | Optional. Name of the workflow (blueprint) to run after upload. The returned `executionId` can be queried with [Get Workflow Execution](/workflows/workflow-executions). |
| abstractId          | integer         | Optional. Abstract to associate with this source                                                                                                                         |

{% tabs %}
{% tab title="201 The source was created" %}

```json
{
    "projectName": "Example Land Project",
    "projectId": 22,
    "directoryName": "Uploads",
    "directoryId": 12,
    "sourceName": "deed.pdf",
    "executionId": 1842,
    "uploadUrl": "https://s3.amazonaws.com/example-bucket/path/to/deed.pdf?X-Amz-Signature=...",
    "pendingSource": {
        "pendingSourceId": 501,
        "name": "deed.pdf",
        "sourceType": "pdf",
        "uploadUrl": "https://s3.amazonaws.com/example-bucket/path/to/deed.pdf?X-Amz-Signature=...",
        "createdAt": "2026-08-20T18:00:00.000Z",
        "expiresAt": "2026-08-20T19:00:00.000Z",
        "confirmedAt": "2026-08-20T19:00:00.000Z",
        "metadata": null,
        "status": "Waiting",
        "finalSourceId": null
    }
}
```

{% endtab %}

{% tab title="403 Forbidden" %}

```json
{
    "message": "You are not permitted to create sources for this project"
}
```

{% endtab %}

{% tab title="404 The pending source was not found" %}

```json
{
    "message": "There is no pending pdf with that name, did you initialize pdf source creation with POST source/init-pdf?"
}
```

{% endtab %}

{% tab title="409 The source was already created" %}

```json
{
    "message": "A pdf source has already been created. If you feel this message is inaccurate, make sure to initialize pdf source creation with source/init-pdf. "
}
```

{% endtab %}
{% endtabs %}

`create-pdf` returns a pending source. Processing continues asynchronously. If a workflow was requested, `executionId` is the id of that [workflow execution](/workflows/workflow-executions). `pendingSource.finalSourceId` is populated once processing finishes; use that id with the [Source Files](/projects-directories-and-sources/source-files) API.

{% tabs %}
{% tab title="Python" %}

```python
create_body = {
  'projectIdentifier': 'Example Land Project',
  'directoryIdentifier': 'Uploads',
  'sourceIdentifier': source_name,
  'ocr': True,
  'ocrProvider': 'textract_plus',
  'workflow': 'land_title'
}

create_response = requests.post(
  'https://api.annolab.ai/v1/source/create-pdf',
  headers=headers,
  json=create_body
)

print(create_response.json())
```

{% endtab %}
{% endtabs %}

## Full example

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'
pdf_path = '/path/to/deed.pdf'

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

# 1. Initialize the pending source and get a signed upload URL
init_response = requests.post(
  'https://api.annolab.ai/v1/source/init-pdf',
  headers=headers,
  json={
    'projectIdentifier': 'Example Land Project',
    'directoryIdentifier': 'Uploads',
    'sourceName': 'deed.pdf'
  }
)
init_json = init_response.json()
upload_url = init_json['uploadUrl']
source_name = init_json['sourceName']

# 2. PUT the PDF to the signed URL (no API key)
with open(pdf_path, 'rb') as pdf_file:
  upload_response = requests.put(upload_url, data=pdf_file)
upload_response.raise_for_status()

# 3. Confirm the upload and start OCR / workflow
create_response = requests.post(
  'https://api.annolab.ai/v1/source/create-pdf',
  headers=headers,
  json={
    'projectIdentifier': 'Example Land Project',
    'directoryIdentifier': 'Uploads',
    'sourceIdentifier': source_name,
    'ocr': True,
    'ocrProvider': 'textract_plus',
    'workflow': 'land_title'
  }
)

print(create_response.json())
```

{% endtab %}
{% endtabs %}


# Annotation Types

Annotation types are the valid annotation labels that can be applied to source material.

## Create Annotation Type

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/annotation-type/create`

Create a new annotation type within your schema

#### Headers

| Name                                            | Type   | Description                                                                                                                                                        |
| ----------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Authorization<mark style="color:red;">\*</mark> | string | <p>Where you put your api key. Creating a schema requires a key with "Write" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                                                | Type    | Description                                                                                                                         |
| --------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| isRelation                                          | boolean | Whether the type being created is intended for use as an annotation relation type. Defaults to False                                |
| color                                               | string  | <p>Hex code for the color of the annotation type<br><code>color: "#D00021B"</code><br>if not provided it will be auto generated</p> |
| typeName<mark style="color:red;">\*</mark>          | string  | Name of the annotation type that will be created                                                                                    |
| projectIdentifier<mark style="color:red;">\*</mark> | string  | Identifier for the project that will contain the annotation type. Either the id or the unique name.                                 |
| category                                            | string  | Category grouping for the annotation type                                                                                           |
| isDocumentClassification                            | boolean | Boolean for whether the annotation type is a document type classification                                                           |

{% tabs %}
{% tab title="201 Annotation type was created successfully" %}

```
{
    "schemaName": "NER",
    "schemaId": 7,
    "key": "place-name",
    "color": "#F882FD",
    "typeId": "51",
    "name": "Place Name"
}
```

{% endtab %}
{% endtabs %}

This code shows how to create a new annotation type

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

annotationType = {
  'projectIdentifier': 'New NER Project',
  'category': 'NER',
  'typeName': 'Place Name' 
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/annotation-type/create'

response = requests.post(url, headers=headers, json=annotationType)

print(response.json())
```

{% endtab %}
{% endtabs %}


# Model Inferences

Inferences are how you request model predictions of annotations on your source files

All model inference requests are asynchronous, meaning you must make the request and then poll for the status.

## Request Inference

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/infer/batch`

Creates an model inference job to be run on one or more source files

#### Headers

| Name                                            | Type   | Description                                                                                                                                                                                        |
| ----------------------------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | string | <p>Where you put your api key. Requesting inferences requires a "Model Run" permission on the project where sources exist<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                                                    | Type            | Description                                                                                            |
| ------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------ |
| projectIdentifier<mark style="color:red;">\*</mark>     | string          | Identifier of the project that contains the source files. Either id or the unique name of the project. |
| modelIdentifier<mark style="color:red;">\*</mark>       | string          | Identifier of the model that will be run. Either id or the unique name of the model.                   |
| sourceIds<mark style="color:red;">\*</mark>             | array           | array of source ids pointing to where models will be run                                               |
| outputLayerIdentifier<mark style="color:red;">\*</mark> | string\|integer | layer in which predictions will be generated                                                           |
| groupName<mark style="color:red;">\*</mark>             | String          | name of the group user belongs to                                                                      |

{% tabs %}
{% tab title="201 The inference job was created and is in queue" %}

```json
{
    "inferenceJobId": 12,
    "status": "Queued",
    "projectName": "Sample Project",
    "projectId": 1,
    "outputLayerName": "Gold Set",
    "outputLayerId": 12,
    "sourceIds": [3240, 4414],
}
```

{% endtab %}

{% tab title="400 The inference could not be started" %}

```
{
    "message": "explanation for why directory could not be created"
}
```

{% endtab %}
{% endtabs %}

## Request Inference Status

<mark style="color:blue;">`GET`</mark> `https://api.annolab.ai/v1/infer/batch/{job_id}`

Returns the status of the inference job

#### Path Parameters

| Name                                    | Type    | Description                                             |
| --------------------------------------- | ------- | ------------------------------------------------------- |
| jobId<mark style="color:red;">\*</mark> | Integer | Integer representing the inference job that was spawned |

#### Headers

| Name                                            | Type   | Description                                                                                                                                                                                        |
| ----------------------------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | string | <p>Where you put your api key. Requesting inferences requires a "Model Run" permission on the project where sources exist<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

{% tabs %}
{% tab title="201 The inference job was created and is in queue" %}

```
{
    "inferenceJobId": 12,
    "status": "Queued",
    "projectId": 1,
    "sourceIds": [3240, 4414]
}
```

{% endtab %}

{% tab title="400 The inference could not be started" %}

```
{
    "message": "explanation for why directory could not be created"
}
```

{% endtab %}

{% tab title="404: Not Found Permissions problem or job doesn't exist" %}

```javascript
{
    // Response
}
```

{% endtab %}
{% endtabs %}

This code shows how to call a specific model on 2 sources, poll status until the model inference is complete, then retrieve the results.

{% tabs %}
{% tab title="Python" %}

```python
import requests
import time

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

inferenceBody = {
  'groupName': 'Company Name',
  'projectIdentifier': 'My Project',
  'sourceIds': [4024, 5853],
  'modelIdentifier': 'Staple + Classify Documents',
  'outputLayerIdentifier': 'Gold Set'
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/infer/batch'

response = requests.post(url, headers=headers, json=inferenceBody)

print(response.json())

get_url = 'https://api.annolab.ai/v1/infer/batch/'+response.json()['inferenceJobId']
maximum_timeout_seconds = 1800
time_taken = 0 
inference_is_finished = False

start_time = time.time()
while not inference_is_finished and time_taken < maximum_timeout_seconds:
  status_response = requests.get(get_url, headers=headers, json=inferenceBody).json()
  if status_response['status'] in ['Finished', 'Errored']:
    print("Inference Finished")
    print(status_response)
    inference_is_finished = True
  time_taken = time.time() - start_time
```

{% endtab %}
{% endtabs %}


# Annotations

Annotations one of the fundamental building blocks of extracted data. They say that a portion of a source has some special meaning

## Create Annotation

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/annotation/create`

Create an annotation of some type on a source file. For text file sources, an annotation may reside over an array of character offsets or may simply be a document level (manual) annotation.

#### Headers

| Name          | Type   | Description                                                                                                                                                                 |
| ------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Creating an annotation requires a key with "Write" permissions.<br><br><code>{"Authorization": "Api-key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

<table><thead><tr><th>Name</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>clientId</td><td>string</td><td>string or integer that will be "passed through" the request. Useful in situations where async or bulk requests are used, and relation inserts will follow annotation inserts.</td></tr><tr><td>offsets</td><td>array</td><td>Array of integers describing where the annotation exists in terms of character offsets inside the source text. Leave undefined for document-level annotations or image annotations.</td></tr><tr><td>preventDuplication</td><td>boolean</td><td>Boolean indicating whether duplication protection should be in place. By default this is set to true.<br><br>Duplication being defined by any annotation with the exact same annotation type, offsets, layer, and source. Usually you only want to set this to false if you have multiple manual annotations (e.g. non-offset based) of the same type that you need to add on a source.</td></tr><tr><td>value</td><td>string</td><td>User defined value associated with the annotation</td></tr><tr><td>directoryIdentifier</td><td>string</td><td>Identifier of the directory containing the source that will have the annotation. Either the id or the unique name.</td></tr><tr><td>annoTypeIdentifier</td><td>string</td><td>Identifier of the annotation type associated with the annotation. Either the id or the unique name.</td></tr><tr><td>projectIdentifier</td><td>string</td><td>Identifier of the project containing the annotation. Either the id or the unique name.</td></tr><tr><td>layerIdentifier</td><td>integer</td><td>Identifier of the layer containing the annotation. Either the id or the unique name.</td></tr><tr><td>sourceIdentifier</td><td>integer</td><td>Identifier of the source where the annotation will be created. Either the id or the unique name.</td></tr><tr><td>textBounds</td><td>Geometry|Null</td><td><p>Polygon or MultiPolygon geometry object describing the location of the text the annotation contains within the page.</p><p>IMPORTANT: Ensure coordinates are in provided clockwise order, ideally from top-left. Otherwise annotations may not render correctly in AnnoLab or other viewers utilizing polygon data.</p><p>Example:</p><pre class="language-json"><code class="lang-json">{
  "type": "MultiPolygon",
  "coordinates": [
    [[
      [ 0.33, 0.32 ],
      [ 0.37, 0.32 ],
      [ 0.37, 0.34 ],
      [ 0.33, 0.34 ],
      [ 0.33, 0.32 ]
    ]],
    [[
      [ 0.376, 0.329 ],
      [ 0.405, 0.329 ],
      [ 0.405, 0.344 ],
      [ 0.376, 0.344 ],
      [ 0.376, 0.329 ]
    ]]
  ],
}
</code></pre></td></tr><tr><td>imageBounds</td><td>Geometry|Null</td><td><p>Polygon or MultiPolygon geometry object describing the location of the box drawn by a model or user on the page (always rectangular).<br><br>IMPORTANT: Ensure coordinates are provided in clockwise order, ideally from top-left. Otherwise annotations may not render correctly in AnnoLab or other viewers utilizing polygon data.<br><br>Example:</p><pre class="language-json"><code class="lang-json">{
  "type": "Polygon",
  "coordinates": [
    [
      [ 0.33, 0.32 ],
      [ 0.37, 0.32 ],
      [ 0.37, 0.34 ],
      [ 0.33, 0.34 ],
      [ 0.33, 0.32 ]
    ]
  ],
}
</code></pre></td></tr></tbody></table>

{% tabs %}
{% tab title="200 Annotation already exists" %}

```
{
  "id": 44,
  "typeId": 51,
  "typeName": "Place Name",
  "layerId": 5,
  "sourceId": 145,
  "typeId": 112,
  "value": '{latitude:"37.983810", longitude:"23.727539"}',
  "rawValue": 'Athens',
  "offsets": [0, 5]
 }
```

{% endtab %}

{% tab title="201 Annotation was successfully created" %}

```
{
  "id": 44,
  "typeId": 51,
  "typeName": "Place Name",
  "layerId": 5,
  "sourceId": 145,
  "typeId": 112,
  "value": '{latitude:"37.983810", longitude:"23.727539"}',
  "rawValue": 'Athens',
  "offsets": [0, 5]
}
```

{% endtab %}

{% tab title="400 Annotation creation failed" %}

```
{
    "message": "Information about why creation failed"
}
```

{% endtab %}
{% endtabs %}

Examples of how to make an annotation create request

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

annotation = {
  'annoTypeIdentifier': 'Place Name',
  'projectIdentifier': 'New NER Project',
  'layerIdentifier': 'NER Gold',
  'sourceIdentifier': 145,
  'offsets': [0, 5],
  'directoryIdentifier': 'Wikipedia Subset',
  'value': '{latitude:"37.983810", longitude:"23.727539"}'
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/annotation/create'

response = requests.post(url, headers=headers, json=annotation)

print(response.json())
```

{% endtab %}
{% endtabs %}

## Bulk Create Annotations

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/annotation/bulk-create`

Create a set of up to 2000 annotations in one request (can be a mix of many different annotation types and spread across many source files).\
\
80% faster than individual inserts in most cases

#### Headers

| Name          | Type   | Description                                                                                                                                                               |
| ------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Creating annotations requires a key with "write" permissions.<br><br><code>{"Authorization": "Api-key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name               | Type    | Description                                                                                                                                                                                                                                                                                                                                                                                |
| ------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| preventDuplication | boolean | <p>Boolean indicating whether duplication protection should be in place. By default this will be set to true<br><br>Duplication being defined by any annotation with the exact same annotation type, offsets, layer, and source.<br><br>We recommend only setting this to false if you are 100% sure that no duplications exist in the request, in which case it will be much quicker.</p> |
| annotations        | array   | Array of annotation objects that you wish to insert.A maximum of 2000 annotations can be created in one request.                                                                                                                                                                                                                                                                           |

{% tabs %}
{% tab title="200 " %}

```
```

{% endtab %}
{% endtabs %}

Examples of how to bulk create annotations

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

bulk_request = {
  'preventDuplication': True
  'annotations': [
    {
      'annoTypeIdentifier': 'Place Name',
      'projectIdentifier': 'New NER Project',
      'schemaIdentifier': 'NER',
      'layerIdentifier': 'NER Gold',
      'sourceIdentifier': 145,
      'offsets': [0, 5],
      'directoryIdentifier': 'Wikipedia Subset',
      'value': '{latitude:"37.983810", longitude:"23.727539"}',
      'clientId': "24a"
    },
    {
      'annoTypeIdentifier': 'Place Name',
      'projectIdentifier': 'New NER Project',
      'schemaIdentifier': 'NER',
      'layerIdentifier': 'NER Gold',
      'sourceIdentifier': 145,
      'offsets': [120, 128],
      'directoryIdentifier': 'Wikipedia Subset',
      'value': '{latitude:"37.983810", longitude:"23.727539"}',
      'clientId': "24b"
    }
  ]
}

url = 'https://api.annolab.ai/v1/annotation/bulk-create'

response = requests.post(url, headers=headers, json=bulk_request)
```

{% endtab %}
{% endtabs %}

## Annotation Object

An object corresponding to a single annotation

<table><thead><tr><th width="193.33333333333331">Attribute Name</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>annotationId</td><td>Integer</td><td>Unique id for the annotation</td></tr><tr><td>sourceReferenceId</td><td>Integer</td><td>Unique id for the file that contains the annotation</td></tr><tr><td>typeName</td><td>String</td><td>Name of the type of annotation</td></tr><tr><td>value</td><td>String</td><td>By default is the text that the annotation contains (can be manually overidden)</td></tr><tr><td>pageNumber</td><td>Integer</td><td>Starting page of the annotation</td></tr><tr><td>endPageNumber</td><td>Integer|Null</td><td>Ending page of the annotation (if single page annotation will be null)</td></tr><tr><td>offsets</td><td>Integer[]</td><td>Array describing character offsets of the annotation within the source</td></tr><tr><td>textBounds</td><td>Geometry|Null</td><td><p>MultiPolygon geometry object describing the location of the text the annotation contains within the page.</p><p>IMPORTANT: Ensure coordinates are in provided clockwise order, ideally from top-left. Otherwise annotations may not render correctly in AnnoLab or other viewers utilizing polygon data.</p><p>Example:</p><pre class="language-json"><code class="lang-json">{
  "type": "MultiPolygon",
  "coordinates": [
    [[
      [ 0.33, 0.32 ],
      [ 0.37, 0.32 ],
      [ 0.37, 0.34 ],
      [ 0.33, 0.34 ],
      [ 0.33, 0.32 ]
    ]],
    [[
      [ 0.376, 0.329 ],
      [ 0.405, 0.329 ],
      [ 0.405, 0.344 ],
      [ 0.376, 0.344 ],
      [ 0.376, 0.329 ]
    ]]
  ],
}
</code></pre></td></tr><tr><td>imageBounds</td><td>Geometry|Null</td><td><p>Polygon or MultiPolygon geometry object describing the location of the box drawn by a model or user on the page (always rectangular).<br><br>IMPORTANT: Ensure coordinates are provided in clockwise order, ideally from top-left. Otherwise annotations may not render correctly in AnnoLab or other viewers utilizing polygon data.<br><br>Example:</p><pre class="language-json"><code class="lang-json">{
  "type": "Polygon",
  "coordinates": [
    [
      [ 0.33, 0.32 ],
      [ 0.37, 0.32 ],
      [ 0.37, 0.34 ],
      [ 0.33, 0.34 ],
      [ 0.33, 0.32 ]
    ]
  ],
}
</code></pre></td></tr><tr><td>createdBy</td><td>Integer</td><td>User id that created the annotation (or user id that invoked the model)</td></tr><tr><td>updatedBy</td><td>Integer</td><td>User Id that updated the annotation</td></tr><tr><td>confidence</td><td>Float</td><td>Value between 0 and 100 representing the confidence of the OCR translation of any text bounds the annotation contains.<br>If annotation contains multiple text bounds, will be an average of all containing texts.</td></tr><tr><td>score</td><td>Float</td><td>Value between 0 and 1 representing sureness of a machine learning model in applying the annotation.<br><br>We calculate this by taking the average of the SoftMax for all tokens comprising the annotation</td></tr><tr><td>layerId</td><td>Integer</td><td>Id for the layer that contains the annotation</td></tr><tr><td>isReviewed</td><td>Boolean</td><td>Has the annotation been reviewed</td></tr><tr><td>reviewedBy</td><td>Integer|Null</td><td>Reviewer that reviewed the annotation</td></tr><tr><td>reviewedAt</td><td>DateTime|Null</td><td>Time of review</td></tr><tr><td>modelSourceId</td><td>Integer</td><td>Id for the model that produced the annotation</td></tr><tr><td>modelSource</td><td>String</td><td>Name of the model that produced the annotation</td></tr></tbody></table>

```json
{
    'annotationId': 403992,
    'sourceReferenceId': 12341, 
    'typeName': 'Grantor', 
    'value': ' Carsuo', 
    'pageNumber': 3, 
    'endPageNumber': null, 
    'offsets': [1320, 1326], 
    'textBounds': {'type': 'MultiPolygon', 'coordinates': [[[[0.565917551517487, 0.769474387168884], [0.609824299812317, 0.769511699676514], [0.609820425510406, 0.778583765029907], [0.565913617610931, 0.778546392917633], [0.565917551517487, 0.769474387168884]]]]}, 
    'imageBounds': null, 
    'createdBy': 5, 
    'updatedBy': null, 
    'confidence': 96.7900390625, 
    'score': 0.7074922025203705, 
    'layerId': 635, 
    'isReviewed': False, 
    'reviewedBy': null, 
    'reviewedAt': null, 
    'modelSourceId': 12, 
    'modelSource': 'Party Name Extractor'
}
```

## CanonicalAnnotation Object

An abstract form of an annotation that is not grounded in any explicit mention or context, simplified to the components of name and value.

These objects cannot be created directly, instead they exist as attributes of [CanonicalTag](/annotations-and-relations/canonical-tags#canonicaltag-object) objects.

<table><thead><tr><th width="166.33333333333331">Attribute Name</th><th width="163">Type</th><th>Description</th></tr></thead><tbody><tr><td>name</td><td>String</td><td>The annotation type name. Must already exist as an annotation type in your project</td></tr><tr><td>value</td><td>String</td><td>Canonical value describing the annotation</td></tr></tbody></table>

```json
{
    "name": "Make",
    "value": "Cessna",
}
```


# Canonical Tags

Unique entities described by a set of annotations

Canonical Tags are a way to reference a singular entity that is comprised of multiple fixed annotations. This is a useful abstraction when dealing with things like Airframes (Make, Model, Serial), People (First, Middle, Last), or Land (Section, Township, Range, QQ1, QQ2, etc). They are intended as a uniquely disambiguating abstraction for the multiple ways that a single entity can be expressed or written in a document.

Canonical Tags exist in two states. In their unattached state as [CanonicalTag](#canonicaltag-object) objects and as [AttachedCanonicalTag](#attachedcanonicaltag-object) object when describing a CanonicalTag that has been attached to an entire instrument or source file.

## CanonicalTag Object

A canonical tag describes a unique entity defined by a type and an array of attributes

| Attribute Name  | Type                                                                                        | Description                                                                                                                                                          |
| --------------- | ------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| typeName        | String                                                                                      | Name describing the type of canonical tag                                                                                                                            |
| attributes      | [CanonicalAnnotation](/annotations-and-relations/annotations#canonicalannotation-object)\[] | Array of canonical annotations that comprise the canonical tag                                                                                                       |
| domainEntityId? | Integer                                                                                     | Unique id describing the canonical tag object                                                                                                                        |
| status?         | String                                                                                      | <p>"Created" or "Found"<br>Returned when creating tags in bulk to reflect whether each tag already exists (Found) or was newly created in the request (Created).</p> |

```json
{
  "typeName": "Airframe Inventory",  
  "attributes": [
      {"name": "Make", "value": "Beech"}, {"name": "Model", "value": "C24R"}, {"name": "Serial Number", "value": "MC-453"}
  ]
}
```

## List the tags in a project

<mark style="color:blue;">`GET`</mark> `https://api.annolab.ai/v1/project/{group_name}/{project_name}/tags`

Returns a paginated list of tags in a project. Returns a limit of 10,000 tags per page/request.

#### Headers

| Name                                            | Type   | Description                                                                                                                                                         |
| ----------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name | Type    | Description                                                                                                                                              |
| ---- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| page | Integer | <p>Which page to return. Defaults to 1.<br>E.g. { "page" : 1 } returns first 1,000 results. { "page" : 2 } returns results 1,001 through 2,000. etc.</p> |

{% tabs %}
{% tab title="200: OK " %}

```json
[{
  "typeName": "Airframe Tag",
  "domainEntityId": 235,
  "attributes": [
      {"name": "Make", "value": "Raytheon"}, 
      {"name": "Model", "value": "850XP"}, 
      {"name": "Serial Number", "value": "755"},
      {"name": "Internal ID", "value": 4501}
  ],
  "createdBy": {"id": 14, "email": "tester@gmail.com", "username": "tester"}
}, {
  "typeName": "Airframe Tag",
  "domainEntityId": 240,
  "attributes": [
      {"name": "Make", "value": "CESSNA"}, 
      {"name": "Model", "value": "T303"}, 
      {"name": "Serial Number", "value": "T30300300"},
      {"name": "Internal ID", "value": 3561}
  ],
  "createdBy": {"id": 14, "email": "tester@gmail.com", "username": "tester"}
}]
```

{% endtab %}
{% endtabs %}

## Create one or more Tags

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/tag`

Creates canonical tags. (Does not attach). If an identical canonical tag already exists, it does not create at duplicate.

#### Headers

| Name                                            | Type | Description                                                                                                                                                         |
| ----------------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> |      | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                                                | Type            | Description                                                                           |
| --------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------- |
| projectIdentifier<mark style="color:red;">\*</mark> | String\|Number  | Project name or id of the project                                                     |
| tags<mark style="color:red;">\*</mark>              | CanonicalTag\[] | List of [CanonicalTag](#canonicaltag-object) objects to create                        |
| groupName                                           | String          | Name of the group that owns the project (only required if projectIdentifer is string) |

{% tabs %}
{% tab title="201: Created Array of created (or found) CanonicalTag objects." %}

```json
[{
  "typeName": "Airframe Tag",
  "domainEntityId": 235,
  "attributes": [
      {"name": "Make", "value": "Raytheon"}, 
      {"name": "Model", "value": "850XP"}, 
      {"name": "Serial Number", "value": "755"},
      {"name": "Internal ID", "value": 4501}
  ],
  "createdBy": {"id": 14, "email": "tester@gmail.com", "username": "tester"}
}, {
  "typeName": "Airframe Tag",
  "domainEntityId": 240,
  "attributes": [
      {"name": "Make", "value": "CESSNA"}, 
      {"name": "Model", "value": "T303"}, 
      {"name": "Serial Number", "value": "T30300300"},
      {"name": "Internal ID", "value": 3561}
  ],
  "createdBy": {"id": 14, "email": "tester@gmail.com", "username": "tester"}
}]
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX' 

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

create_tag_url = 'https://api.annolab.ai/v1/tag'

project_name = "title-demo" 
group_name = "AnnoLab"
tag_list = [
  {
    'typeName': 'Airframe Inventory', 
    'attributes': [
      {'name': 'Make', 'value': 'Cessna'},
      {'name': 'Model', 'value': '501'},
      {'name': 'Serial Number', 'value': '501-0050'}
    ]
  }
]

tagPayload = {
  "projectIdentifier": project_name,
  "groupName": group_name,
  "tags": tag_list
}

r = requests.post(create_tag_url, headers=headers, json=tagPayload)

json_response = r.json()

print(json_response)

```

{% endtab %}
{% endtabs %}

## Edit a tag's values

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/tag/{domain_entity_id}`

Edits a [CanonicalTag](#canonicaltag-object)'s typeName and/or attributes. Does not alter attachments.

#### Headers

| Name                                            | Type   | Description                                                                                                                                                         |
| ----------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                                            | Type                   | Description                                                                                                                                   |
| ----------------------------------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| typeName<mark style="color:red;">\*</mark>      | String                 | Name describing the type of canonical tag                                                                                                     |
| attributes\[]<mark style="color:red;">\*</mark> | CanonicalAnnotation\[] | Array of [CanonicalAnnotation](/annotations-and-relations/annotations#canonicalannotation-object)\[] objects that comprise the canonical tag. |

{% tabs %}
{% tab title="200: OK " %}

```json
{
    "typeName": "Airframe Tag",
    "domainEntityId": 235,
    "attributes": [
        {"name": "Make", "value": "Raytheon"}, 
        {"name": "Model", "value": "850XP"}, 
        {"name": "Serial Number", "value": "755"},
        {"name": "Internal ID", "value": 4501}
    ],
    "createdBy": {"id": 14, "email": "tester@gmail.com", "username": "tester"}
}
```

{% endtab %}
{% endtabs %}

## Delete a single tag by its domainEntityId

<mark style="color:red;">`DELETE`</mark> `https://api.annolab.ai/v1/tag/{domainEntityId}`

Deletes a [CanonicalTag](#canonicaltag-object). Delete will cascade and delete all related [AttachedCanonicalTag](#attachedcanonicaltag-object) objects as well.

#### Headers

| Name                                            | Type   | Description                                                                                                                                                         |
| ----------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

{% tabs %}
{% tab title="200: OK " %}

{% endtab %}
{% endtabs %}

## Bulk delete tags by values

<mark style="color:red;">`DELETE`</mark> `https://api.annolab.ai/v1/tag`

Deletes a [CanonicalTag](#canonicaltag-object). Delete will cascade and delete all related [AttachedCanonicalTag](#attachedcanonicaltag-object) objects as well.

#### Headers

| Name                                            | Type   | Description                                                                                                                                                         |
| ----------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                                                | Type            | Description                                                                                         |
| --------------------------------------------------- | --------------- | --------------------------------------------------------------------------------------------------- |
| projectIdentifier<mark style="color:red;">\*</mark> | String\|Integer | String of the project containing the tag or the unique identifier of the project                    |
| tags<mark style="color:red;">\*</mark>              | CanonicalTag\[] | List of [CanonicalTag](#canonicaltag-object) objects to delete                                      |
| groupName                                           | String          | Name of the group that owns the project (only required if projectIdentifier is a string and not id) |

{% tabs %}
{% tab title="200: OK " %}

```javascript
{
}
```

{% endtab %}
{% endtabs %}

## AttachedCanonicalTag Object

An attached canonical tag object describes the attachment of a canonical tag to some Instrument or Source

<table><thead><tr><th width="206.33333333333331">Attribute Name</th><th width="211">Type</th><th>Description</th></tr></thead><tbody><tr><td>tagType</td><td>String</td><td>What the canonical tag is attached to. Can be attached to either "Instrument" or "Source"</td></tr><tr><td>typeName</td><td>String</td><td>Name describing the type of canonical tag</td></tr><tr><td>domainEntityId</td><td>Integer</td><td>Unique id of the canonical tag</td></tr><tr><td>annotationId?</td><td>Integer</td><td>Unique id describing where the canonical tag is attached. e.g. the unique id of the instrument</td></tr><tr><td>sourceReferenceId?</td><td>Integer</td><td>Unique id describing where the canonical tag is attached. e.g. the unique id of the file</td></tr><tr><td>attributes</td><td><a href="/annotations-and-relations/annotations#canonicalannotation-object">CanonicalAnnotation</a>[]</td><td>Array of canonical annotations that comprise the canonical tag</td></tr><tr><td>createdBy</td><td>UserObject|Null</td><td>Object that consists of a id, email, and username associated with the person who assigned the tag</td></tr></tbody></table>

```json
{
  "tagType": "Instrument", 
  "typeName": "Airframe Inventory", 
  "domainEntityId": 39, 
  "annotationId": 402970, 
  "attributes": [
      {"name": "Make", "value": "Beech"}, {"name": "Model", "value": "C24R"}, {"name": "Serial Number", "value": "MC-453"}
  ],
  "createdBy": {
      "id": 15,
      "email": "tester@gmail.com",
      "usernname": "tester"
  }
}
```

## Attach a tag to an instrument

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/instrument-tag`

Attaches a canonical tag to an instrument.

#### Headers

| Name                                            | Type   | Description                                                                                                                                                         |
| ----------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                                           | Type         | Description                                                |
| ---------------------------------------------- | ------------ | ---------------------------------------------------------- |
| instrumentId<mark style="color:red;">\*</mark> | Integer      | Unique identifier for the instrument you wish to attach to |
| tag<mark style="color:red;">\*</mark>          | CanonicalTag | [CanonicalTag](#canonicaltag-object) object to attach      |

{% tabs %}
{% tab title="201: Created Information about the AttachedCanonicalTag that was created" %}

```javascript
{
  "tagType": "Instrument",
  "typeName": "Airframe Tag",
  "domainEntityId": 235,
  "attributes": [
      {"name": "Make", "value": "Raytheon"}, 
      {"name": "Model", "value": "850XP"}, 
      {"name": "Serial Number", "value": "755"},
      {"name": "Internal ID", "value": 4501}
  ],
  "createdBy": {"id": 14, "email": "tester@gmail.com", "username": "tester"}
}
```

{% endtab %}
{% endtabs %}

## Unattach a tag from an instrument

<mark style="color:red;">`DELETE`</mark> `https://api.annolab.ai/v1/instrument-tag`

Removes a canonical tag attachment from an instrument

#### Headers

| Name                                            | Type   | Description                                                                                                                                                         |
| ----------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                                           | Type         | Description                                                |
| ---------------------------------------------- | ------------ | ---------------------------------------------------------- |
| instrumentId<mark style="color:red;">\*</mark> | Integer      | Unique identifier for the instrument you wish to attach to |
| tag<mark style="color:red;">\*</mark>          | CanonicalTag | [CanonicalTag](#canonicaltag-object) object to delete      |

{% tabs %}
{% tab title="204: No Content " %}

```javascript
{
    // Response
}
```

{% endtab %}
{% endtabs %}

## (Alternate) Unattach a tag from an instrument

<mark style="color:red;">`DELETE`</mark> `https://api.annolab.ai/v1/instrument-tag/{instrument_id}/{domain_entity_id}`

Alternative endpoint to unattach a tag from an instrument using the instrument id and domain entity id of the tag.

An instrument id is equivalent to the annotation id of a classification annotation.

#### Path Parameters

| Name                                             | Type    | Description                                                                                                                            |
| ------------------------------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| instrument\_id<mark style="color:red;">\*</mark> | Integer | The instrument\_id from which to unattach the tag. An instrument id is equivalent to the annotation id of a classification annotation. |
| domain\_entity\_id                               | Integer | The domain\_entity\_id of the canonical tag.                                                                                           |

#### Headers

| Name                                            | Type   | Description                                                                                                                                                         |
| ----------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

{% tabs %}
{% tab title="200: OK The tag was unattached succesfully." %}

{% endtab %}

{% tab title="404: Not Found Either the tag or instrument do not exist, or the tag is not attached to that instrument." %}

{% endtab %}
{% endtabs %}

## Attach a tag to a source

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/source-tag`

Attaches a canonical tag to a source.

#### Headers

| Name                                            | Type   | Description                                                                                                                                                         |
| ----------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                                                | Type              | Description                                                                                                                         |
| --------------------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| projectIdentifier<mark style="color:red;">\*</mark> | String \| Integer | Either the project name or id of the project containing the source. If passing the project name, groupName is a required parameter. |
| groupName                                           | String            | The name of the group which owns the project. Required when passing a project name.                                                 |
| directoryIdentifier                                 | String \| Integer | Name or id of the directory containing the source. Required when passing a source name.                                             |
| sourceIdentifier<mark style="color:red;">\*</mark>  | String \| Integer | Either the source file name or id of the source to attach the tag. If passing source name, directoryIdentifier is required.         |
| tag<mark style="color:red;">\*</mark>               | CanonicalTag      | [CanonicalTag](#canonicaltag-object) object to attach                                                                               |

{% tabs %}
{% tab title="201: Created Information about the newly created attachment" %}

```
{
  "tagType": "Source File",
  "typeName": "Airframe Tag",
  "domainEntityId": 235,
  "attributes": [
      {"name": "Make", "value": "Raytheon"}, 
      {"name": "Model", "value": "850XP"}, 
      {"name": "Serial Number", "value": "755"},
      {"name": "Internal ID", "value": 4501}
  ],
  "createdBy": {"id": 14, "email": "tester@gmail.com", "username": "tester"}
}
```

{% endtab %}
{% endtabs %}

## Unattach a tag from a source.

<mark style="color:red;">`DELETE`</mark> `https://api.annolab.ai/v1/source-tag`

Removes a canonical tag attachment from a source

#### Headers

| Name                                            | Type   | Description                                                                                                                                                         |
| ----------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                                                | Type              | Description                                                                                                                         |
| --------------------------------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| projectIdentifier<mark style="color:red;">\*</mark> | String \| Integer | Either the project name or id of the project containing the source. If passing the project name, groupName is a required parameter. |
| sourceIdentifier<mark style="color:red;">\*</mark>  | String \| Integer | Either the source file name or id of the source to attach the tag. If passing source name, directoryIdentifier is required.         |
| tag<mark style="color:red;">\*</mark>               | CanonicalTag      | [CanonicalTag](#canonicaltag-object) object to attach                                                                               |
| groupName                                           | String            | The name of the group which owns the project. Required when passing a project name.                                                 |
| directoryIdentifier                                 | String \| Integer | Name or id of the directory containing the source. Required when passing a source name.                                             |

{% tabs %}
{% tab title="201: Created " %}

{% endtab %}
{% endtabs %}

## (Alternate) Unattach a tag from a source

<mark style="color:red;">`DELETE`</mark> `https://api.annolab.ai/v1/source-tag/{source_id}/{domain_entity_id}`

Alternative endpoint to unattach a tag from a source using the source id and domain entity id of the tag.

#### Path Parameters

| Name               | Type    | Description                                           |
| ------------------ | ------- | ----------------------------------------------------- |
| source\_id         | Integer | Id of the source file from which to unattach the tag. |
| domain\_entity\_id | Integer | The domain\_entity\_id of the canonical tag.          |

#### Headers

| Name          | Type   | Description                                                                                                                                                         |
| ------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | String | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

{% tabs %}
{% tab title="200: OK The tag was successfully unattached from the source file." %}

{% endtab %}

{% tab title="404: Not Found Either the tag or source do not exist, or the tag is not attached to that source." %}

{% endtab %}
{% endtabs %}


# Land Instruments

Land instruments are returned by the Search a Land Abstract endpoint

## Land Instrument Object

An object corresponding to one land instrument in an abstract. Land instruments include recording information, parties, dates, source-page boundaries, and conveyances contained within the instrument.

#### Attributes

<table><thead><tr><th width="198.33333333333331">Name</th><th width="159">Type</th><th>Description</th></tr></thead><tbody><tr><td>subDocumentId</td><td>Integer</td><td>Unique ID of the instrument.</td></tr><tr><td>startPageNumber</td><td>Integer</td><td>First page of the source file occupied by the instrument.</td></tr><tr><td>startPagePct</td><td>Number</td><td>Vertical position at which the instrument starts on its first page, expressed as a percentage from 0 to 100. Defaults to 0 when no value is stored.</td></tr><tr><td>endPageNumber</td><td>Integer</td><td>Last page of the source file occupied by the instrument.</td></tr><tr><td>endPagePct</td><td>Number</td><td>Vertical position at which the instrument ends on its last page, expressed as a percentage from 0 to 100. Defaults to 100 when no value is stored.</td></tr><tr><td>sourceReferenceId</td><td>Integer</td><td>Unique ID of the source file containing the instrument.</td></tr><tr><td>sourceReferenceName</td><td>String</td><td>Display name or file name of the source reference.</td></tr><tr><td>instrumentType</td><td>String</td><td>Classified instrument type, such as <code>Mineral Deed</code> or <code>Oil and Gas Lease</code>.</td></tr><tr><td>comments</td><td>String[]</td><td>Deduplicated comments extracted from the instrument.</td></tr><tr><td>countyBookNumber</td><td>String</td><td>County recording book number.</td></tr><tr><td>countyPageNumber</td><td>String</td><td>County recording page number.</td></tr><tr><td>instrumentTitle</td><td>String</td><td>Extracted title of the instrument.</td></tr><tr><td>interestTypes</td><td>String[]</td><td>A list of interest types conveyed or modifed in the instrument</td></tr><tr><td>grantorNames</td><td>String[]</td><td>Deduplicated grantor or lessor names.</td></tr><tr><td>granteeNames</td><td>String[]</td><td>Deduplicated grantee or lessee names.</td></tr><tr><td>instrumentNumber</td><td>String</td><td>County instrument or recording number. This property is omitted when no value exists.</td></tr><tr><td>effectiveDate</td><td>String</td><td>Extracted effective date. The text is returned as stored and the property is omitted when no value exists.</td></tr><tr><td>primaryTerm</td><td>String</td><td>Extracted primary term applicable only to leases. This property is omitted when no value exists.</td></tr><tr><td>extensionTerm</td><td>String</td><td>Extracted extension term applicable only to leases. This property is omitted when no value exists.</td></tr><tr><td>royaltyRate</td><td>String</td><td>Extracted royalty rate. This property is omitted when no value exists.</td></tr><tr><td>pughClause</td><td>String</td><td>Extracted Pugh-clause text or value applicable only to leases. This property is omitted when no value exists.</td></tr><tr><td>executedDate</td><td>String</td><td>Extracted execution date. The text is returned as stored and the property is omitted when no value exists.</td></tr><tr><td>fileDate</td><td>String</td><td>Extracted filing date. The text is returned as stored and the property is omitted when no value exists.</td></tr><tr><td>recordedDate</td><td>String</td><td>Extracted recording date. The text is returned as stored and the property is omitted when no value exists.</td></tr><tr><td>referencedInstruments</td><td>String[]</td><td>References to other instruments. Reference instruments are strings in the format of -bk-pg-</td></tr><tr><td>conveyances</td><td><a href="https://docs.annolab.ai/annotations-and-relations/conveyances">Conveyance</a>[]</td><td>Tract-level conveyances located within the instrument. An empty array means that no conveyances exist.</td></tr></tbody></table>

String-array annotations are deduplicated using a case-insensitive, alphanumeric comparison. Returned values retain their stored display text.

```json
{
  "subDocumentId": 10482,
  "startPageNumber": 3,
  "startPagePct": 0,
  "endPageNumber": 7,
  "endPagePct": 100,
  "sourceReferenceId": 615,
  "sourceReferenceName": "2024-001234.pdf",
  "instrumentType": "Mineral Deed",
  "comments": [
    "Legal description continues on Exhibit A"
  ],
  "countyBookNumber": "182",
  "countyPageNumber": "417",
  "instrumentTitle": "Mineral Deed",
  "interestTypes": [
    "Minerals"
  ],
  "grantorNames": [
    "Example Grantor, LLC"
  ],
  "granteeNames": [
    "Example Grantee, LP"
  ],
  "instrumentNumber": "2024-001234",
  "effectiveDate": "01/15/2024",
  "primaryTerm": "3 Years",
  "extensionTerm": "1 Year",
  "royaltyRate": "",
  "pughClause": "",
  "executedDate": "01/15/2024",
  "fileDate": "01/22/2024",
  "recordedDate": "01/22/2024",
  "referencedInstruments": ["Reeves-bk140-pg133-inst#1738"],
  "conveyances": [
    {
      "id": 78301,
      "county": "Reeves",
      "city": "",
      "section": "12",
      "township": "2S",
      "range": "",
      "legalBody": "NW/4",
      "subdivision": "",
      "lot": "",
      "block": "C-8",
      "grossAcres": "160",
      "well": "",
      "abstractId": "42",
      "survey": "Public School Land",
      "depthClause": "Pertaining only to the wolfcamp formation",
      "grantorLessor": ["Example Grantor, LLC"],
      "granteeLessee": "Example Grantee, LP",
      "grantingLanguage": "grants, sells, and conveys",
      "grantedPct": "1/3",
      "grantedBasis": "Entire",
      "interestTypes": ["minerals"],
      "pageNumber": "4",
      "statedNet": "3.25",
      "grantorDomainEntities": [
        {
          "name": "Example Grantor, LLC",
          "address": "100 Main Street, Midland, TX 79701"
        }
      ],
      "granteeDomainEntities": {
        "name": "Example Grantee, LP",
        "address": "200 Market Street, Houston, TX 77002"
      },
      "referenceInstruments": "Reeves-bk140-pg133-inst#1738"
    }
  ]
}
```


# Conveyances

Conveyances describe tract-level interests contained within land instruments

## Conveyance Object

An object corresponding to a tract conveyance contained within a land instrument. A conveyance combines the tract's legal description with the extracted parties, interest, acreage, granting language, and linked party-address data.

#### Attributes

<table><thead><tr><th width="198.33333333333331">Name</th><th width="159">Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>Integer</td><td>Unique conveyance ID</td></tr><tr><td>county</td><td>String</td><td>County named in the legal description.</td></tr><tr><td>city</td><td>String</td><td>City named in the legal description.</td></tr><tr><td>section</td><td>String</td><td>Section identifier.</td></tr><tr><td>township</td><td>String</td><td>Township identifier.</td></tr><tr><td>range</td><td>String</td><td>Range identifier.</td></tr><tr><td>legalBody</td><td>String</td><td>Main legal-description body. This will be the smallest unit of description of the legal and is where quarter calls, metes, and save/except language will be found. When lands are discontinuous they will be delimited with a semicolon.</td></tr><tr><td>subdivision</td><td>String</td><td>Subdivision name.</td></tr><tr><td>lot</td><td>String</td><td>Lot identifier.</td></tr><tr><td>block</td><td>String</td><td>Block identifier.</td></tr><tr><td>grossAcres</td><td>String</td><td>Gross acreage as extracted from the document.</td></tr><tr><td>well</td><td>String</td><td>Well name or identifier.</td></tr><tr><td>abstractId</td><td>String</td><td>Abstract identifier extracted from the conveyance. This ID of the abstract where the file was uploaded.</td></tr><tr><td>survey</td><td>String</td><td>Survey name.</td></tr><tr><td>depthClause</td><td>String</td><td>Depth limitation.</td></tr><tr><td>grantorLessor</td><td>String[]</td><td>Grantor or lessor names attached to the conveyance.</td></tr><tr><td>granteeLessee</td><td>String</td><td>Grantee or lessee name. When linked party data is available, the linked party name is preferred.</td></tr><tr><td>grantingLanguage</td><td>String</td><td>The exact language used to convey. Where things like reservations, wellbore exceptions, and accretion/riparian language will appear.</td></tr><tr><td>grantedPct</td><td>String</td><td>Interest conveyed, returned as extracted text, such as <code>50%</code> or <code>1/2</code> or <code>1.0</code> (meaning 100%).</td></tr><tr><td>grantedBasis</td><td>String</td><td>Basis or category to which the granted interest applies.</td></tr><tr><td>interestTypes</td><td>String[]</td><td>Deduplicated, lowercase interest categories. See <a href="#interest-types">Interest Types</a>.</td></tr><tr><td>pageNumber</td><td>String</td><td>Extracted page number associated with the conveyance.</td></tr><tr><td>statedNet</td><td>String</td><td>When a conveyance has an explicit stated net acreage, extracted text.</td></tr><tr><td>grantorDomainEntities</td><td><a href="#party-with-address-object">PartyWithAddress</a>[]</td><td>Linked grantor parties and their addresses. Returns an empty array when no parties are linked.</td></tr><tr><td>granteeDomainEntities</td><td><a href="#party-with-address-object">PartyWithAddress</a> | null</td><td>Linked grantee party and address, or <code>null</code> when no party is linked.</td></tr><tr><td>referenceInstruments</td><td>String</td><td>Instrument references recorded on the conveyance.</td></tr></tbody></table>

### Interest Types

The `interestTypes` array contains one or more of the following normalized values:

| Value             | Description                            |
| ----------------- | -------------------------------------- |
| minerals          | Mineral interests.                     |
| surface rights    | Rights relating to use of the surface. |
| surface ownership | Ownership of the surface estate.       |
| mineral leasehold | Mineral leasehold interests.           |
| royalties         | Royalty interests.                     |
| wells             | Interests associated with wells.       |
| security interest | Liens or other security interests.     |
| all interest      | All applicable interests.              |

### Party With Address Object

Linked grantor and grantee domain entities use the following shape.

#### Attributes

| Name    | Type   | Description                                                                        |
| ------- | ------ | ---------------------------------------------------------------------------------- |
| name    | String | Normalized linked party name.                                                      |
| address | String | Address associated with the linked party. May be an empty string when unavailable. |

```json
{
  "id": 78301,
  "county": "Reeves",
  "city": "",
  "section": "12",
  "township": "2S",
  "range": "",
  "legalBody": "NW/4",
  "subdivision": "",
  "lot": "",
  "block": "C-8",
  "grossAcres": "160",
  "well": "",
  "abstractId": "42",
  "survey": "Public School Land",
  "depthClause": "Pertaining only to the wolfcamp formation",
  "grantorLessor": ["Example Grantor, LLC"],
  "granteeLessee": "Example Grantee, LP",
  "grantingLanguage": "grants, sells, and conveys",
  "grantedPct": "1/3",
  "grantedBasis": "Entire",
  "interestTypes": ["minerals"],
  "pageNumber": "4",
  "statedNet": "3.25",
  "grantorDomainEntities": [
    {
      "name": "Example Grantor, LLC",
      "address": "100 Main Street, Midland, TX 79701"
    }
  ],
  "granteeDomainEntities": {
    "name": "Example Grantee, LP",
    "address": "200 Market Street, Houston, TX 77002"
  },
  "referenceInstruments": "Reeves-bk140-pg133-inst#1738"
}
```


# Instruments

Instruments are a special format produced by Export Instruments and Search Instruments

## Instrument Object

An object corresponding to one instrument (aka Sub-Document)

#### Attributes

<table><thead><tr><th width="198.33333333333331">Name</th><th width="159">Type</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>Integer</td><td>Unique id for this instrument</td></tr><tr><td>sourceReferenceId</td><td>Integer</td><td>Id of the file that contains the instrument</td></tr><tr><td>sourceName</td><td>String</td><td>Name of the file that contains the instrument</td></tr><tr><td>startPageNumber</td><td>Integer</td><td>Page where the instrument starts</td></tr><tr><td>endPageNumber</td><td>Integer</td><td>Page where the instrument ends</td></tr><tr><td>typeName</td><td>String</td><td>The instrument type (e.g. "Amendment")</td></tr><tr><td>canonicalTags</td><td><a href="/annotations-and-relations/canonical-tags#canonical-tag-object">CanonicalTag</a>[]</td><td>Array of canonical tags associated with the Instrument</td></tr><tr><td>annotations</td><td><a href="/annotations-and-relations/annotations#annotation-object">Annotation</a>[]</td><td>Array of annotations that the instrument contains</td></tr><tr><td>reviewedBy</td><td>UserObject|null</td><td>User object consisting of id, email, and username for the person who last reviewed the instrument</td></tr><tr><td>reviewedAt</td><td>String</td><td>ISO DateTime object of the time which the instrument was last reviewed</td></tr></tbody></table>

```json
{
    'id': 402970, 
    'sourceReferenceId': 1921, 
    'sourceName': "Example_File.pdf", 
    'startPageNumber': 3, 
    'endPageNumber': 5, 
    'typeName': "Bill of Sale",
    'reviewedBy': null,
    'reviewedAt': null, 
    'canonicalTags': [
        {
            'tagType': "Instrument", 
            'typeName': "Airframe Inventory", 
            'domainEntityId': 39, 
            'annotationId': 402970, 
            'attributes': [
                {'name': "Make", 'value': "Beech"}, {'name': "Model", 'value': "C24R"}, {'name': "Serial Number", 'value': "MC-453"}
            ],
            'createdBy': {
                'id': 15,
                'email': "tester@gmail.com",
                'usernname': "tester"
            }
        }
    ], 
    'annotations': [
        {
            'annotationId': 403992,
            'sourceReferenceId': 1921, 
            'typeName': "Grantor", 
            'value': "Carsuo", 
            'pageNumber': 3, 
            'endPageNumber': null, 
            'offsets': [1321, 1326], 
            'textBounds': {'type': 'MultiPolygon', 'coordinates': [[[[0.565917551517487, 0.769474387168884], [0.609824299812317, 0.769511699676514], [0.609820425510406, 0.778583765029907], [0.565913617610931, 0.778546392917633], [0.565917551517487, 0.769474387168884]]]]}, 
            'imageBounds': null, 
            'createdBy': 5, 
            'updatedBy': null, 
            'confidence': 96.7900390625, 
            'score': 0.7074922025203705, 
            'layerId': 635, 
            'isReviewed': False, 
            'reviewedBy': null, 
            'reviewedAt': null, 
            'modelSourceId': 12, 
            'modelSource': "Party Name Extractor"
        }
    ]
}
```


# Relations

## Create Annotation Relation

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/relation/create`

Create a new relation between two annotations. If a relation of the type already exists between the two annotation ids, then the existing relation will be returned and a duplicate will not be created.

#### Headers

| Name          | Type   | Description                                                                                                                                                             |
| ------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Creating a project requires a key with "Write" permissions.<br><br><code>{"Authorization": "Api-key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name               | Type    | Description                                                                                                                                                 |
| ------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| annoTypeIdentifier | string  | Identifier for the annotation type of the relation. Either the id or the unique name. Must be an annotation type with isRelation: true or request will fail |
| projectIdentifier  | string  | Identifier of the project containing the layer. Either the id or the unique name.                                                                           |
| successorId        | integer | id of the annotation that is the child of the relation                                                                                                      |
| predecessorId      | integer | id of the annotation that is the parent of the relation                                                                                                     |
| value              | string  | value of the annotation                                                                                                                                     |

{% tabs %}
{% tab title="200 Relation already exists" %}

```
{
  "id": 72,
  "successorId": 44,
  "predecessorId": 12,
  "typeName": "Coreference",
  "typeId": 112,
  "value": '',
 }
```

{% endtab %}

{% tab title="201 Relation was successfully created" %}

```
{
  "id": 72,
  "successorId": 44,
  "predecessorId": 12,
  "typeName": "Coreference",
  "typeId": 112,
  "value": '',
 }
```

{% endtab %}

{% tab title="400 Relation creation failed" %}

```
{
    "message": "Information about why creation failed"
}
```

{% endtab %}
{% endtabs %}

Examples of how to make a layer create request

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

relation = {
  'relationTypeName': 'Coreference',
  'projectIdentifier': 'New NER Project',
  'successorId': 112,
  'predecessorId': 141,
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/relation/create'

response = requests.post(url, headers=headers, json=relation)

print(response.json())
```

{% endtab %}
{% endtabs %}


# Annotation Layers

Annotation layers are containers for annotations and can be used to "version" annotations and experiment outputs.

## Create Annotation Layer

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/layer/create`

Create a new annotation layer for your project

#### Headers

| Name          | Type   | Description                                                                                                                                                             |
| ------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Creating a project requires a key with "Write" permissions.<br><br><code>{"Authorization": "Api-key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name              | Type    | Description                                                                                                                                                            |
| ----------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| layerName         | string  | Name of the layer that will be created                                                                                                                                 |
| projectIdentifier | string  | Identifier of the project containing the layer. Either the id or the unique name.                                                                                      |
| isGold            | boolean | boolean for whether the layer being created is a "Gold Set" i.e. a layer with data that will be considered truth when compared against annotation or experiment layers |
| description       | string  | Name of the project you wish to create. Must be unique for your group                                                                                                  |

{% tabs %}
{% tab title="201 Project was successfully created" %}

```
{
    "projectName": "New NER Project",
    "projectId": 1,
    "layerName": "NER Gold",
    "id": 5,
    "isGold": true,
    "description": 'Use the NER Schema for this layer. Only "approved" annotations should be kept in this layer'
}
```

{% endtab %}

{% tab title="400 Project creation failed" %}

```
{
    "message": "Information about why creation failed"
}
```

{% endtab %}
{% endtabs %}

Examples of how to make a layer create request

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

layer = {
  'projectIdentifier': 'New NER Project',
  'layerName': 'NER Gold'
  'isGold': True,
  'description': 'Use the NER Schema for this layer. Only "approved" annotations should be kept in this layer'
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/layer/create'

response = requests.post(url, headers=headers, json=layer)

print(response.json())
```

{% endtab %}
{% endtabs %}


# Export Instruments

Export instrument level data so that you can consume the data in your own internal applications

## Export instruments and their data

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/export/instruments`

Export all information related to instruments that meet filter criteria. Can include annotation data, source files, and tags.

#### Headers

| Name                                            | Type   | Description                                                                                                                                                         |
| ----------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                                           | Type    | Description                                                                                                                                                                                                                                                                                                                                                       |
| ---------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| projectOwner<mark style="color:red;">\*</mark> | String  | Group Name that owns the project (viewable in "All Projects" page)                                                                                                                                                                                                                                                                                                |
| projectName<mark style="color:red;">\*</mark>  | String  | Name of the project you wish to export from                                                                                                                                                                                                                                                                                                                       |
| tagFilter                                      | Object  | <p>Filter export to include/exclude instruments with tags of these types attached. Applies to instrument's source file tags as well.</p><p>These filters act as an "OR" condition, so specifying two tags means any instrument with either will be exported.<br><code>{"includeTypes": \["Airframe Inventory"], "excludeTypes": \["Engine Inventory"]}</code></p> |
| sourceFilter                                   | String  | Filters export to only those instruments that have a source file name equal to sourceFilter                                                                                                                                                                                                                                                                       |
| includeFiles                                   | Boolean | If true, splits each instrument into individual files for export                                                                                                                                                                                                                                                                                                  |

{% tabs %}
{% tab title="201: Created Export Request created. Response contains information on how to check status of export request" %}

```javascript
{
    'message': "Export Request Successful. To check export status make a GET request at exportStatusUrl in response body",
    'exportStatusUrl': "https://api.annolab.ai/v1/export/status/341",
    'exportJob': {'id': 341, 'status': "initialized", 'projectId': 148, 'isInstrumentsExport': true}
}
```

{% endtab %}
{% endtabs %}

This code shows how to request an instrument export, then download the export contents.

{% tabs %}
{% tab title="Python" %}

<pre class="language-python"><code class="lang-python">import requests
import time

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

exportRequestBody = {
  'projectOwner': 'Group Name owning project',
  'projectName': 'Example Project Name',
  'tagFilter': {
    'includeTypes': ["Airframe Inventory"],
    'excludeTypes': ["Engine Inventory"],
  },
  'sourceFilter': "Test_PDF.pdf",
  'includeFiles': True
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/export/instruments'

<strong>response = requests.post(url, headers=headers, json=exportRequestBody, stream=True)
</strong>
status_url = response.json()['exportStatusUrl']
export_status = response.json()['exportJob']['status']

if response.status_code == 201:
  while export_status not in ['finished', 'errored']:
    response = requests.get(status_url, headers=headers).json()
    print(response)
    export_status = response['status']
    if export_status not in ['finished', 'errored']:
      time.sleep(15)
  
  if export_status == 'finished':
    signed_url = response['downloadUrl']
    download_response = requests.get(signed_url, stream=True)
    export_file_name = 'example_export.zip'
    if download_response.status_code == 200:
      with open(export_file_name, 'wb') as f:
        for chunk in download_response.iter_content(1024):
          f.write(chunk)
    print("Export download finished see: ", export_file_name)
    
</code></pre>

{% endtab %}
{% endtabs %}

### Example Export Format

Once an export is completed, you will see a zip file on your file system

<figure><img src="https://2539662686-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MR10rNw-iCGaHMIJEII%2Fuploads%2FAa0ymP6cqAzNIfw6XkTV%2FScreen%20Shot%202022-12-18%20at%205.03.49%20PM.png?alt=media&amp;token=c47602c2-2114-4694-bb49-49b1c17082ed" alt=""><figcaption></figcaption></figure>

Opening the zip file will reveal one .json (jsonlines file) and a folder named pdfs (if you specified `includeFiles: true` in your export request)

<figure><img src="https://2539662686-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MR10rNw-iCGaHMIJEII%2Fuploads%2FJnzjMbZfF704JnZ7C0UI%2FScreen%20Shot%202022-12-18%20at%205.09.21%20PM.png?alt=media&amp;token=79e75833-d634-4a16-8ad0-cf16fb42d1e9" alt=""><figcaption><p>example_export.zip extracted</p></figcaption></figure>

Exported instrument pdfs will have a name of the form `<Instrument Type>_<page start>-<page end>_<instrument id>.pdf`

The `instruments.json` file will contain json lines of [Instrument Objects](/annotations-and-relations/instruments#instrument-object)


# Export Project

Export projects so that you can train your own ML models

## Export Project Files

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/export/project`

Export requested project details. Each element of the project will have its own "json lines" file and be included in a zip file package.

#### Headers

| Name          | Type   | Description                                                                                                                                                         |
| ------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                                                | Type            | Description                                                                                                              |
| --------------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------ |
| includeTextBounds                                   | boolean         | boolean of whether you want x,y text bounds of text of any pdfs in project                                               |
| annotationLayerIds                                  | array           | Array of layer ids that you wish to export. defaults to all layers                                                       |
| includeSources                                      | boolean         | boolean of whether you want source files to be include with the export. defaults to false                                |
| annotationLayerNames                                | array           | Array of strings containing the names of annotation layers you wish to be included in the export. defaults to all layers |
| projectIdentifier<mark style="color:red;">\*</mark> | string\|integer | Identifier for the project that will contain the annotation type. Either the id or the unique name.                      |
| includeAnnotationTypes                              | boolean         | Boolean to include annotation types with export. Defaults to false                                                       |
| sourceIds                                           | array           | array of source ids to export within the project                                                                         |

{% tabs %}
{% tab title="200 Export was generated successfully. content will be a zip file containing up to 6 json lines files and sub folders for each directory (if includeSources=True) |-------- <projectName>.annotations.jsonl|-------- <projectName>.atntypes.jsonl|-------- <projectName>.layers.jsonl|-------- <projectname>.relations.jsonl|-------- <projectName>.sources.jsonl" %}

```
{
    headers: {
        'Content-Type': 'application/zip',
        'Content-Disposition': `attachment; filename=New Ner Project.zip`
    }
    content: <byte-stream>
}
```

{% endtab %}
{% endtabs %}

This code shows how to request a project export

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

exportRequestBody = {
  'projectIdentifier': 'New NER Project',
  'includeSources': True,
  'includeAnnotationTypes': True,
  'includeTestBounds': False
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/export/project'

response = requests.post(url, headers=headers, json=exportRequestBody, stream=True)

if r.status_code == 200:
  d = r.headers['content-disposition']
  fileName = re.findall("filename=(.+)", d)[0]
  with open(fileName, 'wb') as f:
    for chunk in r.iter_content(1024):
      f.write(chunk)
```

{% endtab %}
{% endtabs %}


# Search Instruments (deprecated)

Search a project for instruments and annotations that match the search criteria

## Search instruments and their data

<mark style="color:blue;">`GET`</mark> `https://api.annolab.ai/v1/instrument/search`

Search a project for information related to instruments that meet search filter criteria. Returns a maximum of 100 instruments and their annotations per page.\
\
If you need individual pdf files exported for each instrument, try using the [Export Instruments api](/exports/export-instruments)

#### Headers

| Name                                            | Type   | Description                                                                                                                                                         |
| ----------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>Where you put your api key. Exporting a project requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name                                           | Type    | Description                                                                                                                                                                                                                                                                                                                                                        |
| ---------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| projectOwner<mark style="color:red;">\*</mark> | String  | Group Name that owns the project (viewable in "All Projects" page)                                                                                                                                                                                                                                                                                                 |
| projectName<mark style="color:red;">\*</mark>  | String  | Name of the Project where you want to search                                                                                                                                                                                                                                                                                                                       |
| tagFilter                                      | Object  | <p>Filter search to include/exclude instruments with tags of these types attached. Applies to instrument's source file tags as well.<br><br>These filters act as an "OR" condition, so specifying two tags means any instrument with either will be returned.<br><code>{"includeTypes": \["Airframe Inventory"], "excludeTypes": \["Engine Inventory"]}</code></p> |
| sourceFilter                                   | String  | Filters export to only those instruments that have a source file name equal to sourceFilter                                                                                                                                                                                                                                                                        |
| page                                           | Integer | Search result pagination number                                                                                                                                                                                                                                                                                                                                    |

{% tabs %}
{% tab title="201: Created Your search result (100 instruments at a time)" %}

```javascript
{
    'page': 1,
    'hasMorePages': false,
    'results': []
}
```

{% endtab %}
{% endtabs %}

## Search Result Object

| Attribute Name | Type                                                                      | Description                                                          |
| -------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| page           | Integer                                                                   | The page of the search result                                        |
| hasMorePages   | Boolean                                                                   | Whether the search result has additional pages that can be requested |
| results        | [Instrument](/annotations-and-relations/instruments#instrument-object)\[] | Array of Instrument objects that comprise the search result          |

```json
{ 
    'page': 1, 
    'hasMorePages': false, 
    'results': []
}
```

{% tabs %}
{% tab title="Python" %}

```python
import requests
import time

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

searchBody = {
  'projectOwner': 'Group Name owning project',
  'projectName': 'Example Project Name',
  'tagFilter': {
    "includeTypes": ["Airframe Inventory"],
    "excludeTypes": ["Engine Inventory"]}
  },
  'sourceFilter': "Test_PDF.pdf"
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/instrument/search'

response = requests.post(url, headers=headers, json=searchBody, stream=True).json()

search_result = response['results']
#Do something with search result here

#Continue paging your search until there are no more pages 
#(only 100 instruments returned per page)
while response['hasMorePages']:
  searchBody['page'] = response['page'] + 1
  response = requests.get(url, headers=headers, json=searchBody, stream=True).json()
  search_result = response['results']
  #Do something with search result here
  

```

{% endtab %}
{% endtabs %}


# Search Land Abstract

Search a Land Abstract and return the Land Instruments it contains

## Search an abstract for land instruments

<mark style="color:blue;">`POST`</mark> `https://api.annolab.ai/v1/abstract/land/instruments/search`

Returns the land instruments associated with one abstract, including the instrument's extracted recording data, parties, dates, page boundaries, and tract conveyances.

The response is paginated, returning up to 200 instruments at a time. Start with page `1`, then request successive pages while `hasMorePages` is `true`.

#### Headers

| Name                                            | Type   | Description                                                                                                                                                                                                      |
| ----------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization<mark style="color:red;">\*</mark> | String | <p>API key authentication. The key must have the <code>read:all</code> scope, and its owner must have permission to export the project.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |
| Content-Type                                    | String | Must be `application/json`.                                                                                                                                                                                      |

#### Request Body

| Name                                                 | Type              | Description                                                                                             |
| ---------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------- |
| projectIdentifier<mark style="color:red;">\*</mark>  | String \| Integer | Either the name of the AnnoLab project or the the project ID.                                           |
| groupName                                            | String            | Name of the group that owns the project. When omitted, the project is resolved for the API key's owner. |
| abstractIdentifier<mark style="color:red;">\*</mark> | String \| Integer | The name of the Abstract where search will be performed or the ID of the Abstract                       |
| page                                                 | Number            | Page to return. Defaults to `1`.                                                                        |

#### Example Request Body

```json
{
  "projectIdentifier": "Permian Basin Title Project",
  "groupName": "Oil Company 1",
  "abstractIdentifier": "Reeves County Abstract 42",
  "page": 1
}
```

{% tabs %}
{% tab title="200: Search results" %}

```json
{
  "page": 1,
  "hasMorePages": false,
  "results": [
    {
      "subDocumentId": 10482,
      "startPageNumber": 3,
      "startPagePct": 0,
      "endPageNumber": 7,
      "endPagePct": 100,
      "sourceReferenceId": 615,
      "sourceReferenceName": "2024-001234.pdf",
      "instrumentType": "Mineral Deed",
      "comments": ["Legal description continues on Exhibit A"],
      "countyBookNumber": "182",
      "countyPageNumber": "417",
      "instrumentTitle": "Mineral Deed",
      "interestTypes": ["Minerals"],
      "grantorNames": ["Example Grantor, LLC"],
      "granteeNames": ["Example Grantee, LP"],
      "instrumentNumber": "2024-001234",
      "effectiveDate": "01/15/2024",
      "primaryTerm": "3 Years",
      "extensionTerm": "1 Year",
      "royaltyRate": "",
      "pughClause": "",
      "executedDate": "01/15/2024",
      "fileDate": "01/22/2024",
      "recordedDate": "01/22/2024",
      "referencedInstruments": [],
      "conveyances": [
        {
          "id": 78301,
          "county": "Reeves",
          "city": "",
          "section": "12",
          "township": "2S",
          "range": "",
          "legalBody": "NW/4",
          "subdivision": "",
          "lot": "",
          "block": "C-8",
          "grossAcres": "160",
          "well": "",
          "abstractId": "42",
          "survey": "Public School Land",
          "depthClause": "Pertaining only to the wolfcamp formation",
          "grantorLessor": ["Example Grantor, LLC"],
          "granteeLessee": "Example Grantee, LP",
          "grantingLanguage": "grants, sells, and conveys",
          "grantedPct": "1/3",
          "grantedBasis": "Entire",
          "interestTypes": ["minerals"],
          "pageNumber": "4",
          "statedNet": "3.25",
          "grantorDomainEntities": [
            {
              "name": "Example Grantor, LLC",
              "address": "100 Main Street, Midland, TX 79701"
            }
          ],
          "granteeDomainEntities": {
            "name": "Example Grantee, LP",
            "address": "200 Market Street, Houston, TX 77002"
          },
          "referenceInstruments": "Reeves-bk140-pg133-inst#1738"
        }
      ]
    }
  ]
}
```

{% endtab %}
{% endtabs %}

## Search Result Object

| Attribute Name | Type                                                                                        | Description                                   |
| -------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------- |
| page           | Number                                                                                      | Page number returned.                         |
| hasMorePages   | Boolean                                                                                     | Whether another page of results is available. |
| results        | [LandInstrumentJson](https://docs.annolab.ai/annotations-and-relations/land-instruments)\[] | Land instruments in the abstract.             |

## Errors

| Status           | Description                                                                                                      |
| ---------------- | ---------------------------------------------------------------------------------------------------------------- |
| 400 Bad Request  | The request body is invalid, the project does not exist, or the abstract does not exist in the selected project. |
| 401 Unauthorized | The API key is missing or invalid.                                                                               |
| 403 Forbidden    | The API key lacks the required scope or its owner does not have permission to export the project.                |

{% tabs %}
{% tab title="Python" %}

```python
import json
from urllib.error import HTTPError
from urllib.request import Request, urlopen

ANNO_LAB_API_KEY = "XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX"
project = "TX Permian"
groupName = "Oil Company 1"
abstractName = "Section 1 - Blk 22 - Midland TX"
timeout = 120
output_file = "abstract-results.json"

url = "https://api.annolab.ai/v1/abstract/land/instruments/search"
headers = {
    "Authorization": f"Api-Key {ANNO_LAB_API_KEY}",
    "Content-Type": "application/json",
}
search_body = {
    "projectIdentifier": project,
    "groupName": groupName,
    "abstractIdentifier": abstractName,
    "page": 1,
}
all_results = []

while True:

    request = Request(
        url,
        data=json.dumps(search_body).encode(),
        headers=headers,
        method="POST",
    )

    try:
        with urlopen(request, timeout=timeout) as response:
            data = json.load(response)
    except HTTPError as error:
        print(error.read().decode())
        raise SystemExit(1)

    results = data["results"]
    all_results.extend(results)
    print(f"Page {data['page']}: {len(results)} results")

    if not data["hasMorePages"]:
        break

    search_body["page"] = data["page"] + 1


with open(output_file, "w") as file:
    json.dump(all_results, file, indent=2)

print(
    f"Total: {len(all_results)} from {abstractName} results written to {output_file}"
)
```

{% endtab %}
{% endtabs %}


# Abstracts

Abstracts are an ordered collection of instruments and typically represent a title summary.

### Abstract Object

#### Attributes

| Name        | Type                                                                              | Description                                               |
| ----------- | --------------------------------------------------------------------------------- | --------------------------------------------------------- |
| id          | Integer                                                                           | Unique id of the abstract                                 |
| key         | String                                                                            | A unique key for the abstract                             |
| tags        | [CanonicalTag](/annotations-and-relations/canonical-tags#canonical-tag-object)\[] | Tags associated with this abtract.                        |
| instruments | [AbstractInstrument](#abstract-instrument-object)\[]                              | Instruments in this abstract                              |
| projectId   | Integer                                                                           | Id of the abstract's project                              |
| createdAt   | String                                                                            | ISO formatted DateTime when the abstract was created      |
| updatedAt   | String                                                                            | ISO formatted DateTime when the abstract was last updated |
| createdBy   | UserObject\|null                                                                  | Original creator of the abstract                          |
| updatedBy   | UserObject\|null                                                                  | Last user who updated the abstract                        |

### Abstract Instrument Object

## Create Abstract

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/abstract/create`

Create a new abstract in a project. Each area of interest (AOI) is the land models target when examining documents. They are stored as one or more [Legal Description](/annotations-and-relations/canonical-tags) tags on the abstract. Newly created abstracts start with no instruments; instruments are added as they are matched to those tags.

The project must already have a **Legal Description** tag type. Creating an abstract requires an API key with "Write" permissions, and the key's user must have view access to the project.

#### Headers

| Name          | Type   | Description                                                                                                                                                           |
| ------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Creating an abstract requires a key with "Write" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name              | Type                                   | Description                                                                                                                                                                                                                                               |
| ----------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name              | string                                 | Name of the abstract to create                                                                                                                                                                                                                            |
| projectIdentifier | string\|integer                        | Id or unique name of the project that will contain the abstract                                                                                                                                                                                           |
| groupName         | string                                 | Optional. Group that owns the project. When provided, the project is resolved as that group's project named or identified by `projectIdentifier`. Omit to resolve the project from the API key's user.                                                    |
| aois              | [AOI](#area-of-interest-aoi-object)\[] | At least one area of interest. Models use these to extract only information relevant to the target land. Each AOI is converted into Legal Description tags on the abstract. See [Texas AOIs](#texas-aoi) for how a single Texas AOI can produce two tags. |
| subdivisions      | string\[]                              | Optional. Subdivision names to associate with the abstract                                                                                                                                                                                                |

{% tabs %}
{% tab title="201 The abstract was created" %}

```json
{
    "id": 1842,
    "key": "sec-12-t3n-r4w-garvin-ok",
    "projectId": 22,
    "tags": [
        {
            "typeName": "Legal Description",
            "domainEntityId": 901,
            "attributes": [
                {"name": "Section", "value": "12"},
                {"name": "Township", "value": "3N"},
                {"name": "Range", "value": "4W"},
                {"name": "County", "value": "Garvin"},
                {"name": "State", "value": "OK"}
            ]
        }
    ],
    "instruments": [],
    "createdAt": "2026-08-20T18:00:00.000Z",
    "updatedAt": "2026-08-20T18:00:00.000Z",
    "createdBy": {"id": 14, "username": "tester", "email": "tester@gmail.com"},
    "updatedBy": {"id": 14, "username": "tester", "email": "tester@gmail.com"}
}
```

{% endtab %}

{% tab title="400 The abstract could not be created" %}

```json
{
    "message": "Project Example Project does not exist"
}
```

{% endtab %}

{% tab title="403 Forbidden" %}

```json
{
    "message": "You do not have permission to create an abstract in that project."
}
```

{% endtab %}
{% endtabs %}

A `400` is also returned when the project is missing a Legal Description tag type:

```json
{
    "message": "Project is missing a \"Legal Description\" tag type required to store AOIs."
}
```

Example of how to make the request to create an abstract

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

abstract = {
  'projectIdentifier': 'Example Land Project',
  'name': 'Garvin 12-3N-4W',
  'aois': [
    {
      'state': 'OK',
      'county': 'Garvin',
      'section': '12',
      'township': '3N',
      'range': '4W'
    }
  ],
  'subdivisions': ['NW/4']
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/abstract/create'

response = requests.post(url, headers=headers, json=abstract)

print(response.json())
```

{% endtab %}
{% endtabs %}

## Get Predicted Owners

<mark style="color:blue;">`POST`</mark> `https://api.annolab.ai/v1/abstract/land/predicted-owners`

**Ownership predictions require running the ownership chain agent first. See** [**Ownership Chain Agent**](/abstracts/ownership-chain-agent)**.**

Return predicted current owners for a land abstract. Each result is a party ownership prediction with the party name, aliases, addresses, legal description, estimated net acreage, and whether the party is predicted to own an interest.

Requires an API key with "Read" permissions, and the key's user must have view access to the abstract's project.

#### Headers

| Name          | Type   | Description                                                                                                                                                              |
| ------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Authorization | string | <p>Where you put your api key. Getting predicted owners requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

#### Request Body

| Name               | Type            | Description                                                                                                                                       |
| ------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| projectIdentifier  | string\|integer | Id or unique name of the project that contains the abstract                                                                                       |
| abstractIdentifier | string\|integer | Id or name of the abstract                                                                                                                        |
| groupName          | string          | Optional. Group that owns the project. When provided, the project is resolved as that group's project named or identified by `projectIdentifier`. |

{% tabs %}
{% tab title="200 Predicted owners were retrieved" %}

```json
[
    {
        "partyName": "Jane Doe",
        "partyAliases": ["Jane A. Doe"],
        "partyAddresses": [
            {
                "address": "100 Main Street, Pauls Valley, OK 73075",
                "date": "01/15/2024"
            }
        ],
        "legalDescription": "NW/4 (12-3N-4W)",
        "netAcreageEstMin": 40,
        "netAcreageEstMax": 40,
        "interestTypes": ["Mineral"],
        "predictionNotes": "Grantor retained a mineral interest in the NW/4.",
        "confidence": "high",
        "ownsInterest": true
    }
]
```

{% endtab %}

{% tab title="400 The project was not found" %}

```json
{
    "message": "Project 'Example Land Project' does not exist"
}
```

{% endtab %}

{% tab title="403 Forbidden" %}

```json
{
    "message": "You do not have permission to view abstracts in that project."
}
```

{% endtab %}

{% tab title="404 The abstract was not found" %}

```json
{
    "message": "No abstract exists with the identifier <Garvin 12-3N-4W>"
}
```

{% endtab %}
{% endtabs %}

### Predicted Owner Object

| Name             | Type                                                          | Description                                                                                    |
| ---------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| partyName        | String                                                        | Full name of the predicted owner                                                               |
| partyAliases     | String\[]                                                     | Other names associated with the party                                                          |
| partyAddresses   | [Predicted Owner Address](#predicted-owner-address-object)\[] | Addresses for the party, including dates from related conveyances when available               |
| legalDescription | String                                                        | Formatted legal description the prediction applies to, or an empty string if none is available |
| netAcreageEstMin | Number\|null                                                  | Lower bound of the estimated net acres owned                                                   |
| netAcreageEstMax | Number\|null                                                  | Upper bound of the estimated net acres owned                                                   |
| interestTypes    | String\[]                                                     | Predicted interest types, for example `Mineral`                                                |
| predictionNotes  | String                                                        | Explanation of the prediction                                                                  |
| confidence       | String                                                        | Confidence of the prediction                                                                   |
| ownsInterest     | Boolean                                                       | Whether the party is predicted to own an interest                                              |

### Predicted Owner Address Object

| Name    | Type   | Description                                                                                        |
| ------- | ------ | -------------------------------------------------------------------------------------------------- |
| address | String | Street or mailing address                                                                          |
| date    | String | Conveyance effective or file date associated with the address, or an empty string if none is known |

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

body = {
  'projectIdentifier': 'Example Land Project',
  'groupName': 'Example Land Group',
  'abstractIdentifier': 'Garvin 12-3N-4W'
}

url = 'https://api.annolab.ai/v1/abstract/land/predicted-owners'

response = requests.post(url, headers=headers, json=body)

print(response.json())
```

{% endtab %}
{% endtabs %}

## Area of Interest (AOI) Object

An AOI is the target land for an abstract. When models examine documents, they use the AOI to cut through noise and extract only information relevant to that area. The exception is leases: when analyzing a lease, models extract lands both within the AOI and outside lands.

`county` and `state` are always required. `state` must be a two-letter US abbreviation. The remaining fields depend on whether the AOI is in Texas.

Omitted optional fields are stored as empty strings on the resulting Legal Description tag.

### Non-Texas AOI

Use this shape when `state` is any US abbreviation except `TX`.

| Name     | Type   | Description                                      |
| -------- | ------ | ------------------------------------------------ |
| county   | string | County name                                      |
| state    | string | Two-letter US state abbreviation other than `TX` |
| section  | string | Optional PLSS section                            |
| township | string | Optional PLSS township                           |
| range    | string | Optional PLSS range                              |

```json
{
  "state": "OK",
  "county": "Garvin",
  "section": "12",
  "township": "3N",
  "range": "4W"
}
```

Each non-Texas AOI creates one Legal Description tag with Section, Township, Range, County, and State.

### Texas AOI

Use this shape when `state` is `TX`.

| Name     | Type   | Description                            |
| -------- | ------ | -------------------------------------- |
| county   | string | County name                            |
| state    | string | Must be `TX`                           |
| section  | string | Optional section                       |
| block    | string | Optional block                         |
| township | string | Optional township                      |
| abstract | string | Optional abstract number / abstract id |
| survey   | string | Optional survey name                   |

```json
{
  "state": "TX",
  "county": "Midland",
  "section": "18",
  "block": "37",
  "township": "T1S",
  "abstract": "A-123",
  "survey": "T&P RR Co"
}
```

A Texas AOI can create one or two Legal Description tags:

* **Block / township tag** (Section, Block, Township, Survey Name, County, State) when block or township is provided, or when block, township, and abstract are all omitted.
* **Abstract tag** (Section, Abstract Id, Survey Name, County, State) when `abstract` is provided.

If both block/township and abstract are present, both tags are created. If only `abstract` is provided, only the abstract tag is created.

Example of creating an abstract from a Texas AOI

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

abstract = {
  'projectIdentifier': 'Example Land Project',
  'name': 'Midland 18-37',
  'aois': [
    {
      'state': 'TX',
      'county': 'Midland',
      'section': '18',
      'block': '37',
      'township': 'T1S',
      'abstract': 'A-123',
      'survey': 'T&P RR Co'
    }
  ]
}

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/abstract/create'

response = requests.post(url, headers=headers, json=abstract)

print(response.json())
```

{% endtab %}
{% endtabs %}


# Ownership Chain Agent

Launch, inspect, stop, and stream the ownership chain agent for a land abstract.

AnnoLab's ownership chain agent uses the instruments you've uploaded to an abstract, constructs an ownership chain, and predicts current ownership. When a job completes, retrieve the predicted owners with [Get Predicted Owners](/abstracts/abstracts#get-predicted-owners).

## Agent Job Object

| Name         | Type          | Description                                                                                                 |
| ------------ | ------------- | ----------------------------------------------------------------------------------------------------------- |
| id           | Integer       | Unique id of the agent job                                                                                  |
| abstractId   | Integer       | Abstract the job is running against                                                                         |
| agentType    | String\|null  | Agent type. Ownership chain jobs use `OwnershipChain`.                                                      |
| status       | String        | One of `Initialized`, `Launching`, `Running`, `StopRequested`, `Stopped`, `Stalled`, `Errored`, `Completed` |
| errorMessage | String\|null  | Error text when `status` is `Errored`                                                                       |
| jobData      | Object        | Job payload, including optional `progress` counts                                                           |
| createdAt    | String        | ISO formatted DateTime when the job was created                                                             |
| createdBy    | Integer\|null | User id that launched the job                                                                               |
| updatedAt    | String        | ISO formatted DateTime when the job was last updated                                                        |

`jobData.progress` may include `conveyancesAnalyzed`, `totalConveyances`, `partiesAnalyzed`, `partiesRequiringAnalysis`, and `totalParties`.

## Get latest ownership chain job

<mark style="color:blue;">`GET`</mark> `https://api.annolab.ai/v1/agents/ownership-chain/latest-job/{abstractId}`

Return the most recently updated ownership chain agent job for an abstract, or `null` if none exists. Requires an API key with "Read" permissions, and the key's user must have view access to the abstract's project.

#### Path Parameters

| Name       | Type    | Description        |
| ---------- | ------- | ------------------ |
| abstractId | integer | Id of the abstract |

#### Headers

| Name          | Type   | Description                                                                                                                                                            |
| ------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Getting the latest job requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

{% tabs %}
{% tab title="200 The latest job was retrieved" %}

```json
{
    "id": 501,
    "abstractId": 1842,
    "agentType": "OwnershipChain",
    "status": "Running",
    "errorMessage": null,
    "jobData": {
        "progress": {
            "conveyancesAnalyzed": 12,
            "totalConveyances": 40,
            "partiesAnalyzed": 3,
            "partiesRequiringAnalysis": 8,
            "totalParties": 8
        }
    },
    "createdAt": "2026-08-25T18:00:00.000Z",
    "createdBy": 14,
    "updatedAt": "2026-08-25T18:05:00.000Z"
}
```

{% endtab %}

{% tab title="403 Forbidden" %}

```json
{
    "message": "You do not have permission to view abstracts in that project."
}
```

{% endtab %}

{% tab title="404 The abstract was not found" %}

```json
{
    "message": "No abstract exists with the identifier <1842>"
}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/agents/ownership-chain/latest-job/1842'

response = requests.get(url, headers=headers)

print(response.json())
```

{% endtab %}
{% endtabs %}

## Get ownership chain job

<mark style="color:blue;">`GET`</mark> `https://api.annolab.ai/v1/agents/ownership-chain/job/{jobId}`

Return the ownership chain agent job for the given id. Requires an API key with "Read" permissions, and the key's user must have view access to the job's abstract project.

#### Path Parameters

| Name  | Type    | Description         |
| ----- | ------- | ------------------- |
| jobId | integer | Id of the agent job |

#### Headers

| Name          | Type   | Description                                                                                                                                                   |
| ------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Getting a job requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

{% tabs %}
{% tab title="200 The job was retrieved" %}

```json
{
    "id": 501,
    "abstractId": 1842,
    "agentType": "OwnershipChain",
    "status": "Running",
    "errorMessage": null,
    "jobData": {
        "progress": {
            "conveyancesAnalyzed": 12,
            "totalConveyances": 40,
            "partiesAnalyzed": 3,
            "partiesRequiringAnalysis": 8,
            "totalParties": 8
        }
    },
    "createdAt": "2026-08-25T18:00:00.000Z",
    "createdBy": 14,
    "updatedAt": "2026-08-25T18:05:00.000Z"
}
```

{% endtab %}

{% tab title="403 Forbidden" %}

```json
{
    "message": "You do not have permission to view abstracts in that project."
}
```

{% endtab %}

{% tab title="404 The job was not found" %}

```json
{
    "message": "No agent job exists with the identifier <501>"
}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/agents/ownership-chain/job/501'

response = requests.get(url, headers=headers)

print(response.json())
```

{% endtab %}
{% endtabs %}

## Launch ownership chain agent

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/agents/ownership-chain/{abstractId}/launch`

Start an ownership chain agent job for the abstract. Requires an API key with "Write" permissions, and the key's user must be able to run model inference on the abstract's project. Returns the created [Agent Job](#agent-job-object).

#### Path Parameters

| Name       | Type    | Description        |
| ---------- | ------- | ------------------ |
| abstractId | integer | Id of the abstract |

#### Headers

| Name          | Type   | Description                                                                                                                                                          |
| ------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Launching the agent requires a key with "Write" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

{% tabs %}
{% tab title="201 The agent job was launched" %}

```json
{
    "id": 501,
    "abstractId": 1842,
    "agentType": "OwnershipChain",
    "status": "Launching",
    "errorMessage": null,
    "jobData": {},
    "createdAt": "2026-08-25T18:00:00.000Z",
    "createdBy": 14,
    "updatedAt": "2026-08-25T18:00:00.000Z"
}
```

{% endtab %}

{% tab title="403 Forbidden" %}

```json
{
    "message": "You are not permitted to run models in this project."
}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/agents/ownership-chain/1842/launch'

response = requests.post(url, headers=headers)

print(response.json())
```

{% endtab %}
{% endtabs %}

## Stop ownership chain agent

<mark style="color:green;">`POST`</mark> `https://api.annolab.ai/v1/agents/ownership-chain/{abstractId}/stop`

Request that the running ownership chain agent for the abstract stop. Requires an API key with "Write" permissions, and the key's user must be able to run model inference on the abstract's project.

If a running job is found, its status becomes `StopRequested` and a stop command is published to the agent. Returns the updated [Agent Job](#agent-job-object), or `null` if nothing was updated.

#### Path Parameters

| Name       | Type    | Description        |
| ---------- | ------- | ------------------ |
| abstractId | integer | Id of the abstract |

#### Headers

| Name          | Type   | Description                                                                                                                                                         |
| ------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Stopping the agent requires a key with "Write" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/agents/ownership-chain/1842/stop'

response = requests.post(url, headers=headers)

print(response.json())
```

{% endtab %}
{% endtabs %}


# Workflow Executions

Workflow executions track OCR, AI agents, and related tasks running against source files in a project.

A workflow execution is a run of a named workflow (blueprint) against one or more source files. Use this endpoint to check overall status and the status of each task.

## Get Workflow Execution

<mark style="color:blue;">`GET`</mark> `https://api.annolab.ai/v1/workflow/execution/{id}`

Return a workflow execution by id. Requires an API key with "Read" permissions, and the key's user must have view access to the execution's project.

#### Path Parameters

| Name | Type    | Description                                                                                                         |
| ---- | ------- | ------------------------------------------------------------------------------------------------------------------- |
| id   | integer | <p>Id of the workflow execution<br><br><code>url = "<https://api.annolab.ai/v1/workflow/execution/1842>"</code></p> |

#### Headers

| Name          | Type   | Description                                                                                                                                                                  |
| ------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | <p>Where you put your api key. Getting a workflow execution requires a key with "Read" permissions.<br><code>{"Authorization": "Api-Key XXXXXXX-XXXXXXX-XXXXXXX"}</code></p> |

{% tabs %}
{% tab title="200 The execution was retrieved" %}

```json
{
    "id": 1842,
    "workflow": "land_title",
    "status": "Running",
    "projectId": 22,
    "startTime": "2026-08-20T18:00:00.000Z",
    "endTime": null,
    "createdBy": {
        "userId": 14,
        "username": "tester"
    },
    "createdAt": "2026-08-20T18:00:00.000Z",
    "sourceIds": [145, 146],
    "tasks": [
        {
            "id": 901,
            "taskType": "OCR",
            "taskName": "OCR",
            "status": "Completed",
            "startTime": "2026-08-20T18:00:01.000Z",
            "endTime": "2026-08-20T18:01:00.000Z"
        },
        {
            "id": 902,
            "taskType": "AI_AGENT",
            "taskName": "Legal Description Extractor",
            "status": "Running",
            "startTime": "2026-08-20T18:01:01.000Z",
            "endTime": null
        }
    ]
}
```

{% endtab %}

{% tab title="403 Forbidden" %}

```json
{
    "message": "You do not have permission to view that workflow."
}
```

{% endtab %}

{% tab title="404 The execution was not found" %}

```json
{
    "message": "No workflow exists with the id <1842>"
}
```

{% endtab %}
{% endtabs %}

Example of how to retrieve a workflow execution

{% tabs %}
{% tab title="Python" %}

```python
import requests

ANNO_LAB_API_KEY = 'XXXXXXX-XXXXXXX-XXXXXXX-XXXXXXX'

headers = {
  'Authorization': 'Api-Key '+ANNO_LAB_API_KEY,
}

url = 'https://api.annolab.ai/v1/workflow/execution/1842'

response = requests.get(url, headers=headers)

print(response.json())
```

{% endtab %}
{% endtabs %}

## Execution Object

| Name      | Type                                       | Description                                                                                                                                     |
| --------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| id        | Integer                                    | Unique id of the workflow execution                                                                                                             |
| workflow  | String\|null                               | Name of the workflow (blueprint) that was run                                                                                                   |
| status    | String                                     | Overall status inferred from the execution's tasks. See [Execution status](#execution-status).                                                  |
| projectId | Integer                                    | Id of the project that contains this execution                                                                                                  |
| startTime | String\|null                               | ISO formatted DateTime when the execution started                                                                                               |
| endTime   | String\|null                               | ISO formatted DateTime when the execution finished, or `null` if it is still running                                                            |
| createdBy | Object\|null                               | User who started the execution: `{ "userId": Integer, "username": String }`                                                                     |
| createdAt | String                                     | ISO formatted DateTime when the execution was created                                                                                           |
| sourceIds | Integer\[]                                 | Source file ids processed by this execution. These ids can be used with the [Source Files](/projects-directories-and-sources/source-files) API. |
| tasks     | [ExecutionTask](#execution-task-object)\[] | Tasks that make up this execution                                                                                                               |

### Execution status

`status` is inferred from the task list:

| Status       | Meaning                                                                |
| ------------ | ---------------------------------------------------------------------- |
| Completed    | Every task completed                                                   |
| Running      | At least one task is waiting or running, and none have errored         |
| PartialError | Some tasks errored, and others completed or are still in progress      |
| Error        | At least one task errored and none completed                           |
| Cancelled    | Nothing is in progress or errored, and at least one task was cancelled |

## Execution Task Object

| Name      | Type         | Description                                                                                                                      |
| --------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| id        | Integer      | Unique id of the task                                                                                                            |
| taskType  | String       | Public task type. One of `OCR`, `AI_AGENT`, `AUTOTAGGER`, or `IMPORT_ZIPPED_SOURCE`. Inference tasks are returned as `AI_AGENT`. |
| taskName  | String       | For `AI_AGENT` tasks, the model name. For all other tasks, the task type.                                                        |
| status    | String       | Task status. One of `WaitingUnsubmitted`, `WaitingSubmitted`, `Running`, `Completed`, `Errored`, `Cancelled`, or `Unknown`.      |
| startTime | String\|null | ISO formatted DateTime when the task started, or `null` if it has not started                                                    |
| endTime   | String\|null | ISO formatted DateTime when the task finished, or `null` if it has not finished                                                  |


