## GET /api/jobs/{jobId}

**Get the status of a job**

Get the progress and outcome of a job that runs in the background. Actions that take time, such as restarting a VPS or changing nameservers in bulk, answer with `operation.jobId` and `operation.pollUrl`. Call this endpoint with that ID until `data.status` is `completed` or `failed`. When the job finishes, `data.result` holds what it returned. When it fails, `data.error` is set, most often to `Job failed.` `data.progressText` explains the failure when more is known. This endpoint is older than v2. The job is wrapped in `data`. Errors are plain JSON with `code` and `message`, not Problem Details. Domain availability checks (`dcheck_…` IDs) are polled at `GET /api/v2/domains/availability/{jobId}` instead. Web hosting backup restores are polled at the `pollUrl` their action returns.

### Related Endpoints

- `GET /api/v2/routed-ipv6`: List routed IPv6 pools and routes
- `POST /api/v2/routed-ipv6`: Allocate an IPv6 /48 or route a subnet
- `DELETE /api/v2/routed-ipv6/{id}`: Release a routed IPv6 /48

### Headers

- `Authorization`: Bearer $HOSTUP_API_KEY

### Parameters

- `jobId` (path, string, required): The job ID from the action response, `operation.jobId` (`job_…` or `dbj_…`). Letters, digits, `_`, `-` and `:` only. Example: `job_01hxa3b4c5d6e7f8g9h0j1k2m3`

### Request Example

```bash
curl "https://cloud.hostup.se/api/jobs/job_01hxa3b4c5d6e7f8g9h0j1k2m3" \
  -H "Authorization: Bearer $HOSTUP_API_KEY"
```

### Response Schema

- `success` (boolean, required): Always `true` on a 200 response.
  Allowed values: true
- `timestamp` (string, required): When the response was made.
- `requestId` (string, required): ID of this request. Quote it when you contact support.
- `data` (object, required)
- `data.jobId` (string, required): The job ID you asked for.
- `data.queueType` (string, optional, nullable): What kind of resource the job works on: `vm` for a VPS, `domain` or `dns` for domains and DNS, `order` for an order.
- `data.type` (string, optional, nullable): What the job does, for example `restart`, `backup`, `rebuild` or `bulk_dns_add`.
- `data.state` (string, optional, nullable): Detailed state, for example `waiting`, `delayed`, `active`, `completed` or `failed`. Branch on `status` instead.
- `data.status` (string, required): `waiting`: queued, not started. `running`: in progress. `completed` and `failed` are final; stop polling when you see one of them.
  Allowed values: waiting, running, completed, failed
- `data.progress` (number, required): How far the job has come, in percent.
- `data.progressText` (string, optional, nullable): Short English text about the current step, for example `Restarting VM...`.
- `data.statusKey` (string, optional, nullable): Stable key for `progressText`, for clients that show their own translated text.
- `data.progressDetailText` (string, optional, nullable): Extra detail about the current step, when the job reports one.
- `data.progressDetails` (object, optional, nullable): Counters for jobs that work through a list, such as bulk domain changes. Orders and bulk domain renewals also give `items`, one row per order line or domain. `null` when the job reports none.
- `data.result` (any, optional): What the job returned. The shape depends on `type`. `null` until the job has finished. A job that finished with a warning still has `status: completed`; the warning is in `result.warning`.
- `data.error` (string, optional, nullable): `null`, or a short message about an error. A failed job most often says `Job failed.`; ISO downloads say what went wrong with the link. A job that is retrying a step, or that completed after a retry, can show `Job reported an error.` Decide on `status`, not on `error`.
- `data.isCompleted` (boolean, required): `true` when `status` is `completed`.
- `data.isFailed` (boolean, required): `true` when `status` is `failed`.
- `data.isRunning` (boolean, required): `true` when `status` is `running`.
- `data.isWaiting` (boolean, required): `true` when `status` is `waiting`.
- `data.createdAt` (string, optional, nullable): When the job was created.
- `data.finishedAt` (string, optional, nullable): When the job finished. `null` while it runs.

### Responses

#### 200 - The job's current status.
```json
{
  "success": true,
  "timestamp": "2026-11-02T10:15:20.000Z",
  "requestId": "11111111-1111-1111-1111-111111111111",
  "data": {
    "jobId": "job_01hxa3b4c5d6e7f8g9h0j1k2m3",
    "queueType": "vm",
    "type": "restart",
    "state": "active",
    "status": "running",
    "progress": 70,
    "progressText": "Restarting VM...",
    "statusKey": "instance.jobs.restarting_vm",
    "progressDetailText": null,
    "progressDetails": null,
    "result": null,
    "error": null,
    "isCompleted": false,
    "isFailed": false,
    "isRunning": true,
    "isWaiting": false,
    "createdAt": "2026-11-02T10:15:02.000Z",
    "finishedAt": null
  }
}
```

#### 400 - The job ID has characters other than letters, digits, `_`, `-` and `:`.
```json
{
  "error": "Bad Request",
  "message": "Invalid job ID format",
  "code": "BAD_REQUEST",
  "timestamp": "2026-11-02T10:15:20.000Z",
  "requestId": "11111111-1111-1111-1111-111111111111"
}
```

#### 401 - No valid API key was sent.
```json
{
  "error": "Unauthorized",
  "message": "Authentication required",
  "code": "UNAUTHORIZED",
  "timestamp": "2026-11-02T10:15:20.000Z",
  "requestId": "11111111-1111-1111-1111-111111111111"
}
```

#### 403 - The job belongs to another account or to a service your key cannot see.
```json
{
  "error": "Forbidden",
  "message": "Access denied",
  "code": "FORBIDDEN",
  "timestamp": "2026-11-02T10:15:20.000Z",
  "requestId": "11111111-1111-1111-1111-111111111111"
}
```

#### 404 - No job has this ID. Finished jobs are kept only for a limited time. A completed job is kept for at most 24 hours. It goes sooner once it is no longer among the newest 100 completed jobs of its kind, counted over all accounts (20 to 50 for DNS jobs). On a busy day that can take minutes. A failed job is kept for up to 7 days. After a 404 for a job you saw running, check the resource itself. For a backup, list the backups. Web hosting backup restores and domain availability checks (`dcheck_…`) answer 404 here too. Poll them at the `pollUrl` their action returned.
```json
{
  "error": "Not Found",
  "message": "Job not found",
  "code": "NOT_FOUND",
  "timestamp": "2026-11-02T10:15:20.000Z",
  "requestId": "11111111-1111-1111-1111-111111111111"
}
```

#### 429 - Too many requests. Two limits apply. They answer differently. The limit per IP address sends a `Retry-After` header and `retryAfter` in the body, without `code`. Wait that many seconds, then poll again. The API key's own limit of 300 requests per five minutes answers with `code` `API_KEY_RATE_LIMIT_EXCEEDED` and the message `Rate limit exceeded`. It sends no `Retry-After` header and no `retryAfter`. Wait up to five minutes before you poll again.
```json
{
  "error": "Too Many Requests",
  "message": "Rate limit exceeded. Please try again later.",
  "retryAfter": "2026-11-02T10:16:20.000Z",
  "requestId": "11111111-1111-1111-1111-111111111111"
}
```

#### 500 - Something went wrong on our side. Poll again later.
```json
{
  "error": "Internal Server Error",
  "message": "An unexpected error occurred",
  "code": "INTERNAL_ERROR",
  "timestamp": "2026-11-02T10:15:20.000Z",
  "requestId": "11111111-1111-1111-1111-111111111111"
}
```
