# Repository · Migration

Part of AppEngine API Documentation. Full index: https://appengine.appmint.io/documentation.md
## POST /repository/migrate

**Migrate data between collections**

`operationId: RepositoryController_migrateData`

Moves or copies records from one or more source collections into a target collection, optionally mapping fields on the way.

`mode` controls how it runs: `instant` performs it synchronously, `batch` queues it as a job, and `auto` chooses based on volume. A batched migration returns a job id to poll.

**`deleteFromSource` makes this destructive.** With it set, records are removed from the source once migrated — run without it first and check the result before repeating with deletion enabled.

#### Signature

```http
POST /repository/migrate (body) -> The migration result, or a job id when batched
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `RootAdmin`, `RootPowerUser`, `ConfigAdmin`.

#### Notes

- Run once without `deleteFromSource` and verify the target before running again with it.
- A `batch` migration returns a job id — poll `GET /repository/migrate/status/{jobId}` rather than assuming completion.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /repository/migrate/status/{jobId}`
- `POST /repository/migrate/cancel/{jobId}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

The migration to run.

```json
{
  "sources": [
    {
      "datatype": "legacy_product",
      "fieldMap": {
        "title": "name"
      }
    }
  ],
  "targetDatatype": "sf_product",
  "mode": "auto"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The migration result, or a job id when batched |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /repository/migrate/status/{jobId}

**Get migration job status**

`operationId: RepositoryController_getMigrationStatus`

Reports progress for a batched migration — how far it has got and whether it succeeded.

#### Signature

```http
GET /repository/migrate/status/{jobId} (jobId: string) -> The job status
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /repository/migrate/jobs`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `jobId` | path | string | yes | Migration job id, from the migrate response. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The job status |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /repository/migrate/jobs

**List migration jobs**

`operationId: RepositoryController_listMigrationJobs`

Lists migration jobs with optional status filtering and paging — the history of what has been migrated.

#### Signature

```http
GET /repository/migrate/jobs (status?: string, page?: integer, pageSize?: integer) -> Migration jobs
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /repository/migrate/status/{jobId}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `status` | query | string | — | Filter by job status. |
| `page` | query | integer | — |  |
| `pageSize` | query | integer | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Migration jobs |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /repository/migrate/cancel/{jobId}

**Cancel a migration job**

`operationId: RepositoryController_cancelMigrationJob`

Stops a running migration.

Cancelling does not roll back what has already been migrated — records already written to the target stay there, and with `deleteFromSource` set, records already removed stay removed. Check the job status to see how far it got.

#### Signature

```http
POST /repository/migrate/cancel/{jobId} (jobId: string) -> The cancellation result
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`). Required role(s): `RootAdmin`, `RootPowerUser`.

#### Notes

- No rollback. A cancelled migration leaves partially-migrated data behind.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /repository/migrate/status/{jobId}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `jobId` | path | string | yes | Migration job id. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The cancellation result |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

