---
title: Batch Operations API
description: Bulk create, update, delete, and transform operations for efficient large-scale data management in TraceRTM
---

# Batch Operations API

The Batch Operations API enables efficient bulk operations on TraceRTM resources. Perform large-scale creates, updates, deletes, and transformations in single API calls with transaction support and detailed progress tracking.

## Endpoints Overview

| Method | Endpoint | Description |
|--------|----------|-------------|
| `POST` | `/projects/{project_id}/batch` | Execute batch operation |
| `GET` | `/projects/{project_id}/batch/{job_id}` | Get batch job status |
| `POST` | `/projects/{project_id}/batch/{job_id}/cancel` | Cancel batch job |
| `GET` | `/projects/{project_id}/batch/history` | List batch job history |
| `POST` | `/projects/{project_id}/batch/validate` | Validate batch operation |

---

## Execute Batch Operation

### `POST /projects/{project_id}/batch`

Execute a batch operation containing multiple actions.

#### Request Body

```json
{
  "operations": [
    {
      "action": "create",
      "resource": "item",
      "data": {
        "type": "requirement",
        "title": "REQ-001: User Authentication",
        "description": "System shall support user authentication",
        "status": "draft",
        "priority": "high"
      },
      "reference_id": "op_1"
    },
    {
      "action": "create",
      "resource": "item",
      "data": {
        "type": "test_case",
        "title": "TC-001: Verify Login",
        "description": "Test user login functionality",
        "status": "draft"
      },
      "reference_id": "op_2"
    },
    {
      "action": "create",
      "resource": "link",
      "data": {
        "source_id": "$ref:op_1",
        "target_id": "$ref:op_2",
        "type": "verified_by"
      },
      "reference_id": "op_3"
    }
  ],
  "options": {
    "mode": "transactional",
    "continue_on_error": false,
    "validate_only": false,
    "notify": true,
    "async": false
  }
}
```

#### Operation Actions

| Action | Description | Supported Resources |
|--------|-------------|---------------------|
| `create` | Create new resource | items, links, comments |
| `update` | Update existing resource | items, links |
| `delete` | Delete resource | items, links, comments |
| `patch` | Partial update | items |
| `move` | Move to different parent/project | items |
| `clone` | Duplicate resource | items |
| `link` | Create relationship | items |
| `unlink` | Remove relationship | items |
| `transition` | Change workflow status | items |
| `assign` | Assign to user | items |
| `tag` | Add/remove tags | items |

#### Batch Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `mode` | string | `transactional` | `transactional` (all-or-nothing) or `independent` |
| `continue_on_error` | boolean | `false` | Continue processing after errors (independent mode) |
| `validate_only` | boolean | `false` | Validate without executing |
| `notify` | boolean | `true` | Send notifications for changes |
| `async` | boolean | `false` | Process asynchronously (returns job ID) |
| `dry_run` | boolean | `false` | Simulate execution, report what would happen |
| `batch_size` | integer | `100` | Operations per batch (async mode) |

#### Reference Variables

Use `$ref:reference_id` to reference results from earlier operations:

```json
{
  "operations": [
    {
      "action": "create",
      "resource": "item",
      "data": { "type": "requirement", "title": "Parent Requirement" },
      "reference_id": "parent"
    },
    {
      "action": "create",
      "resource": "item",
      "data": {
        "type": "requirement",
        "title": "Child Requirement",
        "parent_id": "$ref:parent"
      },
      "reference_id": "child"
    },
    {
      "action": "create",
      "resource": "link",
      "data": {
        "source_id": "$ref:parent",
        "target_id": "$ref:child",
        "type": "parent_of"
      }
    }
  ]
}
```

#### Synchronous Response

```json
{
  "status": "completed",
  "summary": {
    "total": 3,
    "succeeded": 3,
    "failed": 0,
    "skipped": 0
  },
  "results": [
    {
      "reference_id": "op_1",
      "action": "create",
      "resource": "item",
      "status": "success",
      "result": {
        "id": "item_req_new001",
        "type": "requirement",
        "title": "REQ-001: User Authentication"
      }
    },
    {
      "reference_id": "op_2",
      "action": "create",
      "resource": "item",
      "status": "success",
      "result": {
        "id": "item_tc_new001",
        "type": "test_case",
        "title": "TC-001: Verify Login"
      }
    },
    {
      "reference_id": "op_3",
      "action": "create",
      "resource": "link",
      "status": "success",
      "result": {
        "id": "link_new001",
        "source_id": "item_req_new001",
        "target_id": "item_tc_new001",
        "type": "verified_by"
      }
    }
  ],
  "timing": {
    "started_at": "2024-01-20T10:00:00Z",
    "completed_at": "2024-01-20T10:00:02Z",
    "duration_ms": 2100
  }
}
```

#### Asynchronous Response

When `async: true`, returns a job ID for tracking:

```json
{
  "job_id": "batch_job_abc123",
  "status": "queued",
  "operations_count": 150,
  "estimated_duration_seconds": 45,
  "status_url": "/projects/proj_abc123/batch/batch_job_abc123",
  "webhook_url": "https://api.tracertm.dev/webhooks/batch/batch_job_abc123"
}
```

---

## Batch Job Status

### `GET /projects/{project_id}/batch/{job_id}`

Get the status of an asynchronous batch job.

#### Response

```json
{
  "job_id": "batch_job_abc123",
  "status": "processing",
  "progress": {
    "total": 150,
    "processed": 85,
    "succeeded": 82,
    "failed": 3,
    "skipped": 0,
    "percentage": 56.7
  },
  "current_operation": {
    "index": 86,
    "action": "update",
    "resource": "item",
    "reference_id": "op_86"
  },
  "errors": [
    {
      "index": 23,
      "reference_id": "op_23",
      "action": "update",
      "error": {
        "code": "ITEM_NOT_FOUND",
        "message": "Item not found: item_invalid123"
      }
    },
    {
      "index": 45,
      "reference_id": "op_45",
      "action": "create",
      "error": {
        "code": "VALIDATION_ERROR",
        "message": "Required field 'title' is missing"
      }
    }
  ],
  "timing": {
    "queued_at": "2024-01-20T10:00:00Z",
    "started_at": "2024-01-20T10:00:05Z",
    "estimated_completion": "2024-01-20T10:01:30Z"
  },
  "options": {
    "mode": "independent",
    "continue_on_error": true
  }
}
```

#### Job Statuses

| Status | Description |
|--------|-------------|
| `queued` | Job is waiting to be processed |
| `processing` | Job is currently executing |
| `completed` | Job finished successfully |
| `completed_with_errors` | Job finished with some failures |
| `failed` | Job failed completely |
| `cancelled` | Job was cancelled |
| `expired` | Job results expired (after 7 days) |

---

## Cancel Batch Job

### `POST /projects/{project_id}/batch/{job_id}/cancel`

Cancel a running or queued batch job.

#### Request Body

```json
{
  "rollback": true,
  "reason": "Discovered incorrect data in source file"
}
```

#### Response

```json
{
  "job_id": "batch_job_abc123",
  "status": "cancelling",
  "operations_completed": 85,
  "operations_rolled_back": 82,
  "rollback_status": "in_progress",
  "message": "Cancellation initiated, rollback in progress"
}
```

---

## Batch Job History

### `GET /projects/{project_id}/batch/history`

List historical batch jobs.

#### Query Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `status` | string | Filter by status |
| `action` | string | Filter by action type |
| `created_by` | string | Filter by creator |
| `start_date` | string | Filter by start date |
| `end_date` | string | Filter by end date |
| `limit` | integer | Results per page (default: 20) |
| `offset` | integer | Pagination offset |

#### Response

```json
{
  "jobs": [
    {
      "job_id": "batch_job_abc123",
      "status": "completed",
      "summary": {
        "total": 150,
        "succeeded": 150,
        "failed": 0
      },
      "actions": ["create", "link"],
      "created_by": "user_abc123",
      "created_at": "2024-01-20T10:00:00Z",
      "completed_at": "2024-01-20T10:02:30Z",
      "duration_ms": 150000
    },
    {
      "job_id": "batch_job_def456",
      "status": "completed_with_errors",
      "summary": {
        "total": 500,
        "succeeded": 487,
        "failed": 13
      },
      "actions": ["update"],
      "created_by": "user_def456",
      "created_at": "2024-01-19T15:00:00Z",
      "completed_at": "2024-01-19T15:05:45Z",
      "duration_ms": 345000
    }
  ],
  "total": 47,
  "limit": 20,
  "offset": 0
}
```

---

## Validate Batch Operation

### `POST /projects/{project_id}/batch/validate`

Validate a batch operation without executing it.

#### Request Body

```json
{
  "operations": [
    {
      "action": "update",
      "resource": "item",
      "data": {
        "id": "item_req_001",
        "status": "approved"
      }
    },
    {
      "action": "delete",
      "resource": "item",
      "data": {
        "id": "item_req_002"
      }
    }
  ]
}
```

#### Response

```json
{
  "valid": false,
  "summary": {
    "total": 2,
    "valid": 1,
    "invalid": 1,
    "warnings": 1
  },
  "results": [
    {
      "index": 0,
      "action": "update",
      "valid": true,
      "warnings": [
        {
          "code": "WORKFLOW_TRANSITION",
          "message": "This status change will trigger workflow notifications"
        }
      ]
    },
    {
      "index": 1,
      "action": "delete",
      "valid": false,
      "errors": [
        {
          "code": "HAS_DEPENDENCIES",
          "message": "Item has 5 incoming links that would be orphaned",
          "details": {
            "link_count": 5,
            "link_types": ["verified_by", "traces_to"]
          }
        }
      ]
    }
  ],
  "recommendations": [
    {
      "operation_index": 1,
      "suggestion": "Add operations to remove or reassign the 5 incoming links before deleting this item"
    }
  ]
}
```

---

## Common Batch Patterns

### Bulk Create Items

```json
{
  "operations": [
    {
      "action": "create",
      "resource": "item",
      "data": {
        "type": "requirement",
        "title": "REQ-001: Feature A",
        "status": "draft",
        "tags": ["mvp", "phase-1"]
      }
    },
    {
      "action": "create",
      "resource": "item",
      "data": {
        "type": "requirement",
        "title": "REQ-002: Feature B",
        "status": "draft",
        "tags": ["mvp", "phase-1"]
      }
    }
  ],
  "options": {
    "mode": "transactional"
  }
}
```

### Bulk Update Status

```json
{
  "operations": [
    {
      "action": "transition",
      "resource": "item",
      "data": {
        "id": "item_req_001",
        "to_status": "approved",
        "comment": "Approved in batch review"
      }
    },
    {
      "action": "transition",
      "resource": "item",
      "data": {
        "id": "item_req_002",
        "to_status": "approved",
        "comment": "Approved in batch review"
      }
    }
  ],
  "options": {
    "mode": "independent",
    "continue_on_error": true
  }
}
```

### Bulk Assign Items

```json
{
  "operations": [
    {
      "action": "assign",
      "resource": "item",
      "data": {
        "id": "item_req_001",
        "assignee": "user_abc123"
      }
    },
    {
      "action": "assign",
      "resource": "item",
      "data": {
        "id": "item_req_002",
        "assignee": "user_abc123"
      }
    }
  ]
}
```

### Bulk Tag Operations

```json
{
  "operations": [
    {
      "action": "tag",
      "resource": "item",
      "data": {
        "id": "item_req_001",
        "add_tags": ["reviewed", "sprint-5"],
        "remove_tags": ["needs-review"]
      }
    },
    {
      "action": "tag",
      "resource": "item",
      "data": {
        "id": "item_req_002",
        "add_tags": ["reviewed", "sprint-5"],
        "remove_tags": ["needs-review"]
      }
    }
  ]
}
```

### Clone Items with Links

```json
{
  "operations": [
    {
      "action": "clone",
      "resource": "item",
      "data": {
        "source_id": "item_req_001",
        "title_prefix": "[COPY] ",
        "include_links": true,
        "include_attachments": false,
        "include_comments": false,
        "target_status": "draft"
      },
      "reference_id": "cloned_req"
    }
  ]
}
```

### Import from External System

```json
{
  "operations": [
    {
      "action": "create",
      "resource": "item",
      "data": {
        "type": "requirement",
        "title": "Imported: JIRA-1234",
        "description": "Original description from Jira",
        "external_id": "JIRA-1234",
        "external_system": "jira",
        "custom_fields": {
          "jira_key": "JIRA-1234",
          "jira_project": "PROJ",
          "import_date": "2024-01-20"
        }
      },
      "reference_id": "jira_1234"
    },
    {
      "action": "create",
      "resource": "item",
      "data": {
        "type": "requirement",
        "title": "Imported: JIRA-1235",
        "description": "Depends on JIRA-1234",
        "external_id": "JIRA-1235",
        "external_system": "jira"
      },
      "reference_id": "jira_1235"
    },
    {
      "action": "create",
      "resource": "link",
      "data": {
        "source_id": "$ref:jira_1235",
        "target_id": "$ref:jira_1234",
        "type": "depends_on",
        "metadata": {
          "imported_from": "jira"
        }
      }
    }
  ],
  "options": {
    "mode": "transactional"
  }
}
```

---

## SDK Examples

### Python

```python
from tracertm import TraceRTMClient
from tracertm.batch import BatchOperation, BatchOptions

client = TraceRTMClient(api_key="rtm_live_sk_...")

# Simple batch create
operations = [
    BatchOperation.create("item", {
        "type": "requirement",
        "title": f"REQ-{i:03d}: Feature {i}",
        "status": "draft"
    }, reference_id=f"req_{i}")
    for i in range(1, 11)
]

result = client.batch_execute(
    project_id="proj_abc123",
    operations=operations,
    options=BatchOptions(mode="transactional")
)

print(f"Created {result.summary.succeeded} items")

# Batch with references
operations = [
    BatchOperation.create("item", {
        "type": "requirement",
        "title": "Parent Requirement"
    }, reference_id="parent"),

    BatchOperation.create("item", {
        "type": "test_case",
        "title": "Test Case"
    }, reference_id="test"),

    BatchOperation.link(
        source="$ref:parent",
        target="$ref:test",
        link_type="verified_by"
    )
]

result = client.batch_execute(
    project_id="proj_abc123",
    operations=operations
)

# Access created IDs
parent_id = result.get_result("parent").id
test_id = result.get_result("test").id
print(f"Created requirement {parent_id} with test {test_id}")

# Async batch for large operations
operations = [
    BatchOperation.update("item", {"id": item_id, "status": "approved"})
    for item_id in large_item_list  # 1000+ items
]

job = client.batch_execute_async(
    project_id="proj_abc123",
    operations=operations,
    options=BatchOptions(
        mode="independent",
        continue_on_error=True,
        batch_size=100
    )
)

print(f"Job started: {job.job_id}")

# Poll for completion
import time
while job.status in ["queued", "processing"]:
    time.sleep(5)
    job = client.batch_get_status(
        project_id="proj_abc123",
        job_id=job.job_id
    )
    print(f"Progress: {job.progress.percentage}%")

print(f"Completed: {job.progress.succeeded} succeeded, {job.progress.failed} failed")

# Validate before executing
validation = client.batch_validate(
    project_id="proj_abc123",
    operations=operations
)

if not validation.valid:
    print("Validation errors:")
    for result in validation.results:
        if not result.valid:
            print(f"  Operation {result.index}: {result.errors}")
else:
    print("All operations valid, proceeding...")

# Bulk tag update
items_to_tag = ["item_001", "item_002", "item_003"]
operations = [
    BatchOperation.tag(item_id, add_tags=["reviewed"], remove_tags=["pending"])
    for item_id in items_to_tag
]

client.batch_execute(project_id="proj_abc123", operations=operations)
```

### TypeScript

```typescript
import { TraceRTMClient, BatchOperation, BatchOptions } from '@tracertm/sdk';

const client = new TraceRTMClient({ apiKey: 'rtm_live_sk_...' });

// Simple batch create
const operations = Array.from({ length: 10 }, (_, i) =>
  BatchOperation.create('item', {
    type: 'requirement',
    title: `REQ-${String(i + 1).padStart(3, '0')}: Feature ${i + 1}`,
    status: 'draft'
  }, { referenceId: `req_${i + 1}` })
);

const result = await client.batchExecute({
  projectId: 'proj_abc123',
  operations,
  options: { mode: 'transactional' }
});

console.log(`Created ${result.summary.succeeded} items`);

// Batch with references
const linkedOperations = [
  BatchOperation.create('item', {
    type: 'requirement',
    title: 'Parent Requirement'
  }, { referenceId: 'parent' }),

  BatchOperation.create('item', {
    type: 'test_case',
    title: 'Test Case'
  }, { referenceId: 'test' }),

  BatchOperation.link('$ref:parent', '$ref:test', 'verified_by')
];

const linkedResult = await client.batchExecute({
  projectId: 'proj_abc123',
  operations: linkedOperations
});

// Async batch for large operations
const largeOperations = itemIds.map(id =>
  BatchOperation.update('item', { id, status: 'approved' })
);

const job = await client.batchExecuteAsync({
  projectId: 'proj_abc123',
  operations: largeOperations,
  options: {
    mode: 'independent',
    continueOnError: true,
    batchSize: 100
  }
});

console.log(`Job started: ${job.jobId}`);

// Poll for completion
let status = job;
while (['queued', 'processing'].includes(status.status)) {
  await new Promise(resolve => setTimeout(resolve, 5000));
  status = await client.batchGetStatus({
    projectId: 'proj_abc123',
    jobId: job.jobId
  });
  console.log(`Progress: ${status.progress.percentage}%`);
}

// Handle errors
if (status.errors?.length > 0) {
  console.log('Errors encountered:');
  status.errors.forEach(err => {
    console.log(`  Operation ${err.index}: ${err.error.message}`);
  });
}

// Cancel a running job
await client.batchCancel({
  projectId: 'proj_abc123',
  jobId: job.jobId,
  rollback: true,
  reason: 'Found data issues'
});
```

### Go

```go
package main

import (
    "context"
    "fmt"
    "time"
    "github.com/tracertm/tracertm-go"
)

func main() {
    client := tracertm.NewClient("rtm_live_sk_...")
    ctx := context.Background()

    // Create batch operations
    operations := make([]tracertm.BatchOperation, 10)
    for i := 0; i < 10; i++ {
        operations[i] = tracertm.BatchOperation{
            Action:   "create",
            Resource: "item",
            Data: map[string]interface{}{
                "type":   "requirement",
                "title":  fmt.Sprintf("REQ-%03d: Feature %d", i+1, i+1),
                "status": "draft",
            },
            ReferenceID: fmt.Sprintf("req_%d", i+1),
        }
    }

    // Execute synchronously
    result, err := client.BatchExecute(ctx, &tracertm.BatchRequest{
        ProjectID:  "proj_abc123",
        Operations: operations,
        Options: tracertm.BatchOptions{
            Mode: "transactional",
        },
    })
    if err != nil {
        panic(err)
    }

    fmt.Printf("Created %d items\n", result.Summary.Succeeded)

    // Execute async for large batches
    job, err := client.BatchExecuteAsync(ctx, &tracertm.BatchRequest{
        ProjectID:  "proj_abc123",
        Operations: largeOperations,
        Options: tracertm.BatchOptions{
            Mode:            "independent",
            ContinueOnError: true,
            BatchSize:       100,
        },
    })
    if err != nil {
        panic(err)
    }

    fmt.Printf("Job started: %s\n", job.JobID)

    // Poll for completion
    for job.Status == "queued" || job.Status == "processing" {
        time.Sleep(5 * time.Second)
        job, err = client.BatchGetStatus(ctx, &tracertm.BatchStatusRequest{
            ProjectID: "proj_abc123",
            JobID:     job.JobID,
        })
        if err != nil {
            panic(err)
        }
        fmt.Printf("Progress: %.1f%%\n", job.Progress.Percentage)
    }

    fmt.Printf("Completed: %d succeeded, %d failed\n",
        job.Progress.Succeeded, job.Progress.Failed)

    // Validate operations
    validation, err := client.BatchValidate(ctx, &tracertm.BatchValidateRequest{
        ProjectID:  "proj_abc123",
        Operations: operations,
    })
    if err != nil {
        panic(err)
    }

    if !validation.Valid {
        fmt.Println("Validation errors found:")
        for _, r := range validation.Results {
            if !r.Valid {
                fmt.Printf("  Operation %d: %v\n", r.Index, r.Errors)
            }
        }
    }
}
```

---

## Error Handling

### Partial Failure (Independent Mode)

```json
{
  "status": "completed_with_errors",
  "summary": {
    "total": 100,
    "succeeded": 97,
    "failed": 3,
    "skipped": 0
  },
  "errors": [
    {
      "index": 23,
      "reference_id": "op_23",
      "action": "update",
      "error": {
        "code": "ITEM_NOT_FOUND",
        "message": "Item not found: item_invalid123"
      }
    },
    {
      "index": 45,
      "reference_id": "op_45",
      "action": "create",
      "error": {
        "code": "VALIDATION_ERROR",
        "message": "Required field 'title' is missing"
      }
    },
    {
      "index": 78,
      "reference_id": "op_78",
      "action": "transition",
      "error": {
        "code": "INVALID_TRANSITION",
        "message": "Cannot transition from 'approved' to 'draft'"
      }
    }
  ]
}
```

### Transaction Rollback

```json
{
  "status": "failed",
  "summary": {
    "total": 100,
    "succeeded": 0,
    "failed": 1,
    "rolled_back": 45
  },
  "error": {
    "index": 46,
    "reference_id": "op_46",
    "action": "create",
    "error": {
      "code": "DUPLICATE_EXTERNAL_ID",
      "message": "Item with external_id 'JIRA-1234' already exists"
    }
  },
  "rollback_info": {
    "operations_rolled_back": 45,
    "rollback_completed_at": "2024-01-20T10:00:15Z"
  }
}
```

---

## Rate Limits and Quotas

| Plan | Operations/Request | Async Job Size | Concurrent Jobs |
|------|-------------------|----------------|-----------------|
| Free | 50 | 500 | 1 |
| Pro | 200 | 5,000 | 5 |
| Enterprise | 1,000 | 50,000 | 20 |

---

## Best Practices

### Performance Optimization

1. **Use async for large batches**: Operations > 100 should use async mode
2. **Batch related operations**: Group operations that reference each other
3. **Use appropriate batch sizes**: 50-100 operations per batch is optimal
4. **Validate first**: Use `validate_only` for critical imports

### Error Handling

1. **Use independent mode** for operations that can partially succeed
2. **Use transactional mode** for operations that must all succeed together
3. **Check validation results** before executing large batches
4. **Implement retry logic** for transient failures

### Data Integrity

1. **Use references** to link related items created in the same batch
2. **Validate external IDs** to prevent duplicates
3. **Test with dry_run** before production imports

---

## Related Documentation

- [Items API](/docs/api-reference/rest-api/items) - Individual item operations
- [Links API](/docs/api-reference/rest-api/links) - Link management
- [Webhooks](/docs/api-reference/rest-api/webhooks) - Batch completion notifications
- [Import/Export](/docs/wiki/guides/migration-guide) - Data migration guides
