# Retrieve a sync log

`GET https://stylor.ai/api/v1/datasets/{datasetId}/sources/{sourceId}/logs/{logId}`

- Authentication: private API key, sent as `Authorization: Bearer sgpt-sk-…::…`
- Rate-limit resource: `api.v1.datasets.sources.logs.retrieve`
- Demo key: not accepted
- Web page: https://stylor.ai/guides/rest/v1/retrieve-sync-log

Returns one sync run with its full entry list. Use the `logId` from a finished
job, or from **List sync logs**.

A log with `status: running` belongs to a job that is still processing; its
`stats` and `logs` grow as the run progresses, so it is safe to poll.

## Example request

```bash
curl -X GET "https://stylor.ai/api/v1/datasets/YOUR_DATASET_ID/sources/YOUR_SOURCE_ID/logs/YOUR_LOG_ID" \
  -H "Authorization: Bearer $STYLOR_API_KEY"
```

## Path parameters

- `datasetId` (string (uuid), required): The dataset the source belongs to.
- `sourceId` (string (uuid), required): The source the log belongs to.
- `logId` (string (uuid), required): The log to retrieve.

## Responses

### 200

The log.

Content type `application/json`:

- `success` (boolean, required): Always `true` on a 2xx response.
- `log` (SyncLog, required): The record of one sync run. Created when a worker starts fetching (`running`), then finalised as `completed` or `failed` with counters and, on failure, an `error`. Logs are retained indefinitely and are the durable history of a source; jobs are purged after 7 days.
  - `logId` (string (uuid), required): Unique identifier of the log. Referenced by `SyncJob.logId`. [example `"d9f1b3a5-7c2e-4d6f-8a1b-3c5e7f9a1b2d"`]
  - `sourceId` (string (uuid), required): The source that was synced. [example `"8a4d2f1e-3c5b-4e6a-9d7f-2b1c3d4e5f6a"`]
  - `organizationId` (string (uuid), required): The organization that owns the source. [example `"5b7e9c1a-2d4f-4a6b-8c1d-9e2f3a4b5c6d"`]
  - `datasetId` (string (uuid), required): The dataset the source feeds. [example `"3f1c2a9e-6b7d-4c1e-9a2b-1d4e5f6a7b8c"`]
  - `status` (string, required): `running` while the worker is still fetching or diffing; `completed` or `failed` once finished. [one of `"running"`, `"completed"`, `"failed"`]
  - `stats` (SyncStats, required): Counters for one sync run. `retrieved` is how many products the store returned; the other four describe what the run did with them in the dataset's ingest queue. Items are processed asynchronously afterwards, so `added` and `updated` count queued changes rather than items that are already searchable.
    - `retrieved` (integer, required): Products fetched from the store (capped at 25,000 per run). [minimum 0; example `1284`]
    - `added` (integer, required): Products not previously in the dataset that were queued for import. [minimum 0; example `12`]
    - `updated` (integer, required): Existing products whose store data changed and were queued for re-processing. [minimum 0; example `37`]
    - `archived` (integer, required): Dataset items whose SKU no longer appears in the store, marked archived. [minimum 0; example `3`]
    - `failed` (integer, required): Products that could not be queued. Details are in the log's `error`-level entries. [minimum 0; example `0`]
  - `error` (string): The failure reason. Present only when `status` is `failed`. [example `"Shopify API access forbidden (403)"`]
  - `startedAt` (string (date-time), required): When the run began. Lists are sorted by this field, newest first. [example `"2026-09-11T02:58:40.005Z"`]
  - `completedAt` (string (date-time)): When the run finished. Absent while `running`. [example `"2026-09-11T03:00:12.418Z"`]
  - `createdAt` (string (date-time), required): When the log record was created. [example `"2026-09-11T02:58:40.006Z"`]
  - `updatedAt` (string (date-time), required): When the log last changed. [example `"2026-09-11T03:00:12.418Z"`]
  - `logs` (array<SyncLogEntry>, required): Chronological entries written during the run. Empty array if none.
    - `timestamp` (string (date-time), required): When the entry was written. [example `"2026-09-11T02:59:31.880Z"`]
    - `level` (string, required): Severity. Treat any value outside this list (for example `warn`, which the completion entry uses when items failed) as `warning`. [one of `"info"`, `"success"`, `"warning"`, `"error"`; example `"info"`]
    - `message` (string, required): Human-readable progress or error text. [example `"Retrieved 1284 products"`]

Example:

```json
{
  "success": true,
  "log": {
    "logId": "d9f1b3a5-7c2e-4d6f-8a1b-3c5e7f9a1b2d",
    "sourceId": "8a4d2f1e-3c5b-4e6a-9d7f-2b1c3d4e5f6a",
    "organizationId": "5b7e9c1a-2d4f-4a6b-8c1d-9e2f3a4b5c6d",
    "datasetId": "3f1c2a9e-6b7d-4c1e-9a2b-1d4e5f6a7b8c",
    "status": "completed",
    "stats": {
      "retrieved": 1284,
      "added": 12,
      "updated": 37,
      "archived": 3,
      "failed": 0
    },
    "startedAt": "2026-09-11T02:58:40.005Z",
    "completedAt": "2026-09-11T03:00:12.418Z",
    "createdAt": "2026-09-11T02:58:40.006Z",
    "updatedAt": "2026-09-11T03:00:12.418Z",
    "logs": [
      {
        "timestamp": "2026-09-11T02:58:40.005Z",
        "level": "info",
        "message": "Sync started by worker v1-sync-9c0f1a2b-3d4e-4f5a-8b6c-7d8e9f0a1b2c"
      },
      {
        "timestamp": "2026-09-11T02:59:31.880Z",
        "level": "info",
        "message": "Retrieved 1284 products"
      },
      {
        "timestamp": "2026-09-11T03:00:01.412Z",
        "level": "info",
        "message": "Processing batch 1/3 (500/1284 ops)"
      },
      {
        "timestamp": "2026-09-11T03:00:12.417Z",
        "level": "info",
        "message": "Sync completed: 12 added, 37 updated, 3 archived, 0 failed"
      }
    ]
  }
}
```

## Errors

Every error is a JSON object with an `error` string. Match on the status code; the message is written for people.

| Status | Message | When |
| --- | --- | --- |
| 404 | Sync log not found or access denied | No log with that `logId` belongs to the organization that owns the key under the `datasetId` and `sourceId` in the path. |

### Shared authentication, quota and rate-limit errors

| Status | Message | When |
| --- | --- | --- |
| 401 | API key missing. | The `Authorization` header is absent, is not `Bearer <key>`, or the key does not have three dash-separated segments. |
| 401 | Authentication failed: public key is invalid or not recognized. | A public-key endpoint was called with a key that does not exist. |
| 401 | Authentication failed: private key provided instead of a public key. | A public-key endpoint was called with an `sgpt-sk-…` key. |
| 401 | Authentication failed: token is not recognized. | A private-key endpoint was called with a key whose lookup segment does not exist. |
| 401 | Authentication failed: private key is invalid. | A private-key endpoint was called with a key whose secret does not match. |
| 401 | Authentication failed: public key provided instead of a private key. | A private-key endpoint was called with an `sgpt-pk-…` key. |
| 401 | Authentication failed: bearer token format is invalid. | A private key was sent without its `::` lookup segment. |
| 401 | Authentication failed: API key could not be read. Regenerate the key. | The key's embedded organization data could not be decrypted or parsed. |
| 403 | Demo mode is not available for private-key routes. Demos are only supported with public API keys. | The demo key was sent to a private-key endpoint. |
| 403 | Demo mode is not supported on this API route. Check the documentation for available demo endpoints. | The demo key was sent to a public-key endpoint that opts out of demo mode. |
| 403 | No active subscription for this organization. | The organization that owns the key has no active subscription. |
| 403 | No quota found for organization "<organizationId>". | The organization has no quota record for the current billing period. |
| 403 | No entitlement for "<resource>". | The plan's quota template has no entry for this endpoint. |
| 403 | Access denied to "<resource>". | The plan's quota template turns this endpoint off. |
| 429 | Monthly quota exceeded for "<resource>" (<quota>/<quota> used). | The billing-period quota for this endpoint is exhausted. `Retry-After` gives the seconds until the period resets. |
| 429 | RPM limit exceeded for "<resource>" (<used>/<limit> used). | More than the allowed requests per minute were sent. `Retry-After` is 60. |
| 500 | Internal server error. | An unexpected failure on the server. The message is always this string; details are logged, never returned. Quota consumed by the request is refunded, as it is for every 5xx response. |
