# Start record import

Start a resumable import from an uploaded NDJSON file. Apply one approved Upsert operation to each row, using permanent external references to avoid duplicate records. Returns a job ID for progress and row outcomes.

POST https://api.brigg.app/v1/operations/integrations.StartRecordImport

## Access & visibility

Allow `integrations.StartRecordImport` on your API key.

**Workspace access by default.** Private member data requires [approved member delegation](https://developer.brigg.app/authentication#member-delegation) through `Brigg-Act-As`; the key supplies permissions while private ownership and project boundaries remain enforced.

[Set up authentication](https://developer.brigg.app/authentication) · [Choose permissions](https://developer.brigg.app/permissions)

## Request fields

Send a JSON object with the following fields. Required fields must be included; omit optional fields you do not need.
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `file_id` | string | Yes | Uploaded NDJSON file ID. Each physical line is one JSON request body; CSV must be converted first. At least 1 character. |
| `operation` | string | Yes | Published generated Upsert operation, such as customers.UpsertPerson. This credential must separately grant that operation and its resource type. At least 1 character. |

## Prepare, upload and start

Use a published **generated Upsert** operation for the target resource. Business commands with authored or platform handlers cannot be imported this way. Approve both Start record import and that exact Upsert operation on the credential, including its resource read/write grants.

Create a UTF-8 NDJSON file: one complete Upsert request per physical line, with no header or surrounding array. For example:

```json
{"external_ref":{"namespace":"crm-production","id":"person-1042"},"data":{"display_name":"Alex Morgan","email":"alex@example.com"}}
{"external_ref":{"namespace":"crm-production","id":"person-1043"},"data":{"display_name":"Sam Lee","email":"sam@example.com"}}
```

[Upload the file](https://developer.brigg.app/files), then supply its returned file_id and the operation name. Brigg reads the pinned upload version. A private source file requires private:true on every row; choose workspace visibility for a file intended to create shared records. CSV/TSV must be converted to this explicit format first; contact preparation can help with contact-specific mapping.

## Track progress and recover

Poll Get record import and page through List record import rows. Row numbers refer to physical input lines, starting at 1. A completed job can include failed rows: inspect the failed count. Validation failures are recorded and skipped; source access, key revocation, quota and service failures pause the job before the unfinished row.

Each successful record and its resume position commit together. After addressing a pause, call Resume record import with a **new** idempotency key. A lost response to a particular control request uses that request's original key. A stopped worker can be resumed after its lease expires; a queued job can be safely dispatched again. Cancellation stops subsequent commits and retains previously saved records; cancelled jobs cannot resume.

Use the same credential and member context to inspect or control this job. Work never acquires permissions added after it started: every row intersects the original approved grant with current access. If you replace the credential, start a new job with the same external references. The permanent reference matches already imported records even after request receipts expire. Correct invalid rows in a new file, keeping those references; Brigg does not guess a match from email or name.

## Response

Returns **200 OK** with a JSON body.
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `job_id` | string | Yes | Stable identifier of this resumable import. |
| `operation` | string | Yes | Published Upsert operation applied to every row. |
| `file_id` | string | Yes | Immutable NDJSON input file. |
| `status` | string | Yes | paused requires ResumeRecordImport after the cause has been addressed. Completed imports may contain failed rows. Allowed: `"queued"`, `"running"`, `"completed"`, `"paused"`, `"cancelled"`. |
| `processed` | integer | Yes | Rows with a retained outcome. Minimum: 0. |
| `created` | integer | Yes | Rows whose successful upsert created a record. Minimum: 0. |
| `updated` | integer | Yes | Rows whose successful upsert matched an existing record. Minimum: 0. |
| `failed` | integer | Yes | Invalid or refused rows; inspect ListRecordImportRows and submit corrected rows with the same external references. Minimum: 0. |
| `error_code` | string or null | Yes | Reason the job paused, or null. Credentials, source-file access and workspace quotas are checked as the import progresses. |

## Errors & retries

Set `BRIGG_REQUEST_ID` once for each logical action. The samples send it as `Idempotency-Key`. Reuse the same value and body on retries, including after a timeout; a missing response does not prove the action failed.

For rate limits, follow the server’s retry guidance and back off. See [HTTP errors](https://developer.brigg.app/http#errors) and [safe retries and limits](https://developer.brigg.app/reliability).

| HTTP status | Code | What to do |
| --- | --- | --- |
| 400 | `invalid_json` | Send a JSON object with Content-Type: application/json. |
| 400 | `invalid_input` | Check the request fields and types. Supply a stable Idempotency-Key containing 1–200 characters. |
| 401 | `invalid_key` | The workspace API key is missing, invalid or revoked. |
| 403 | `forbidden` | Grant both Start record import and the target Upsert operation, with its required resources and member context. |
| 404 | `not_found` | The requested operation or resource is not available to this credential. |
| 429 | `rate_limited` | Wait for Retry-After before retrying. Reuse the original Idempotency-Key. |
| 422 | `upsert_operation_required` | Choose an available generated Upsert operation, not a Create operation or an authored business command. |
| 422 | `immutable_upload_required` | Complete the file upload first and pass its returned file_id. |

## Next steps

- [Get record import](https://developer.brigg.app/reference/integrations/GetRecordImport)
- [List record import rows](https://developer.brigg.app/reference/integrations/ListRecordImportRows)
- [Resume record import](https://developer.brigg.app/reference/integrations/ResumeRecordImport)
- [Cancel record import](https://developer.brigg.app/reference/integrations/CancelRecordImport)
- [Upsert person](https://developer.brigg.app/reference/customers/UpsertPerson)
- [Create upload](https://developer.brigg.app/reference/documents/CreateUpload)

[Download OpenAPI](https://developer.brigg.app/openapi.json) · [Versioning & retirement](https://developer.brigg.app/versioning)

## Examples

### Customer synchronization

Fictional source and job IDs. Use the IDs and exact operation returned for your workspace.
#### cURL


```sh
curl 'https://api.brigg.app/v1/operations/integrations.StartRecordImport' \
  -H "Authorization: Bearer $BRIGG_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $BRIGG_REQUEST_ID" \
  --data-raw '{
  "file_id": "f_customers",
  "operation": "customers.UpsertPerson"
}'
```

#### TypeScript


```typescript
const requestId = process.env.BRIGG_REQUEST_ID;
if (!requestId) throw new Error("Set BRIGG_REQUEST_ID once per action and reuse it on retries.");

const response = await fetch("https://api.brigg.app/v1/operations/integrations.StartRecordImport", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BRIGG_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": requestId, // Create once per action; reuse on retries.
  },
  body: JSON.stringify({
    "file_id": "f_customers",
    "operation": "customers.UpsertPerson"
  }),
});
const result = await response.json();
if (!response.ok) throw new Error(`${response.status}: ${result.error} — ${result.message}`);
console.log(result);
```

#### Python


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

request_id = os.environ["BRIGG_REQUEST_ID"]  # Reuse for retries of this action.
payload = {
  "file_id": "f_customers",
  "operation": "customers.UpsertPerson"
}
request = Request(
    "https://api.brigg.app/v1/operations/integrations.StartRecordImport",
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {os.environ['BRIGG_API_KEY']}",
        "Content-Type": "application/json",
        "Idempotency-Key": request_id,  # Create once per action; reuse on retries.
    },
    method="POST",
)
try:
    with urlopen(request) as response:
        print(json.load(response))
except HTTPError as error:
    print(error.code, json.load(error))
    raise
```

#### Response example


```json
{
  "job_id": "job_customer_sync",
  "operation": "customers.UpsertPerson",
  "file_id": "f_customers",
  "status": "queued",
  "processed": 0,
  "created": 0,
  "updated": 0,
  "failed": 0,
  "error_code": null
}
```

## JSON schemas


```json-schema-request
{
  "type": "object",
  "properties": {
    "file_id": {
      "type": "string",
      "minLength": 1,
      "description": "Uploaded NDJSON file ID. Each physical line is one JSON request body; CSV must be converted first."
    },
    "operation": {
      "type": "string",
      "minLength": 1,
      "description": "Published generated Upsert operation, such as customers.UpsertPerson. This credential must separately grant that operation and its resource type."
    }
  },
  "required": [
    "file_id",
    "operation"
  ],
  "additionalProperties": false,
  "examples": [
    {
      "file_id": "f_customers",
      "operation": "customers.UpsertPerson"
    }
  ]
}
```


```json-schema-response
{
  "type": "object",
  "properties": {
    "job_id": {
      "type": "string",
      "description": "Stable identifier of this resumable import."
    },
    "operation": {
      "type": "string",
      "description": "Published Upsert operation applied to every row."
    },
    "file_id": {
      "type": "string",
      "description": "Immutable NDJSON input file."
    },
    "status": {
      "type": "string",
      "enum": [
        "queued",
        "running",
        "completed",
        "paused",
        "cancelled"
      ],
      "description": "paused requires ResumeRecordImport after the cause has been addressed. Completed imports may contain failed rows."
    },
    "processed": {
      "type": "integer",
      "minimum": 0,
      "description": "Rows with a retained outcome."
    },
    "created": {
      "type": "integer",
      "minimum": 0,
      "description": "Rows whose successful upsert created a record."
    },
    "updated": {
      "type": "integer",
      "minimum": 0,
      "description": "Rows whose successful upsert matched an existing record."
    },
    "failed": {
      "type": "integer",
      "minimum": 0,
      "description": "Invalid or refused rows; inspect ListRecordImportRows and submit corrected rows with the same external references."
    },
    "error_code": {
      "type": [
        "string",
        "null"
      ],
      "description": "Reason the job paused, or null. Credentials, source-file access and workspace quotas are checked as the import progresses."
    }
  },
  "required": [
    "job_id",
    "operation",
    "file_id",
    "status",
    "processed",
    "created",
    "updated",
    "failed",
    "error_code"
  ],
  "additionalProperties": false,
  "examples": [
    {
      "job_id": "job_customer_sync",
      "operation": "customers.UpsertPerson",
      "file_id": "f_customers",
      "status": "queued",
      "processed": 0,
      "created": 0,
      "updated": 0,
      "failed": 0,
      "error_code": null
    }
  ]
}
```

