# ProImport API - How-To Guide for Sellers

This guide helps sellers connect to the Tradera ProImport API to manage car advertisements programmatically.

## Table of Contents

1. [Getting Started](#getting-started)
2. [Authentication](#authentication)
3. [Base URL](#base-url)
4. [Data Model](#data-model)
5. [API Endpoints](#api-endpoints)
6. [Vehicle Reference Data](#vehicle-reference-data)
7. [Example Workflows](#example-workflows)
8. [Error Handling](#error-handling)
9. [Best Practices](#best-practices)

## Getting Started

The ProImport API allows you to create, update, retrieve, and delete car advertisements on Tradera. All requests are made via HTTP and require authentication.

### Prerequisites

- API credentials (JWT token)
- Dealer code assigned to your account
- Vehicle data and images ready for upload

### Supported Categories

- **1020** - Cars (Personbilar)
- **1021** - Transport Vehicles (Transportbilar)

## Authentication

All API requests require authentication using a JWT token passed in the `X-Auth-Token` header.

```http
X-Auth-Token: your_jwt_token_here
```

## Base URL

### Production
```
https://api-v2.tradera.com/pro-import-api/v3/ad
```

### Development
```
https://api-v2.dev-external.tradera.dev/pro-import-api/v3/ad
```

**Note:** All examples in this guide use the production URL. For development/testing, replace `api-v2.tradera.com` with `api-v2.dev-external.tradera.dev`.

## Data Model

### Ad Request Structure

When creating or updating an ad, you'll work with this structure:

```json
{
  "source_id": "your-unique-id-123",
  "dealer_code": "YOUR_DEALER_CODE",
  "category_id": 1020,
  "visible": true,
  "title": "Optional custom title",
  "body": "Ad description text",
  "price": [
    {
      "currency": "SEK",
      "type": "list",
      "amount": 350000
    }
  ],
  "image_urls": [
    "https://example.com/image1.jpg",
    "https://example.com/image2.jpg"
  ],
  "category_fields": {
    "model_year": 2020,
    "body_type": "suv",
    "brand": "volvo",
    "model": "xc90",
    "model_type": "D5 AWD",
    "registration_number": "ABC123",
    "color": "black",
    "powertrain": {
      "transmission": "automatic",
      "fuels": ["diesel"]
    },
    "condition": {
      "mileage": {
        "unit": "km",
        "value": 45000
      }
    },
    "equipments": ["navigation", "leather_seats"],
    "warranty": "12 months warranty",
    "service_history": "Full service history available",
    "home_delivery": "Home delivery available",
    "return_policy": "30-day return policy"
  },
  "location": {
    "latitude": 59.3293,
    "longitude": 18.0686
  },
  "url": "https://yourwebsite.com/cars/123",
  "dont_publish_on_failed_images": false
}
```

### Required Fields

- **source_id** - Your unique identifier for this ad
- **dealer_code** - Your dealer code
- **category_id** - Must be 1020 (Cars) or 1021 (Transport)
- **body** - Ad description text
- **price** - List with at least one price object
- **category_fields** - All the car-specific fields

### Category Fields (for Cars)

Required fields within `category_fields`:
- **model_year** - Year of manufacture
- **body_type** - Body type (e.g., "suv", "sedan", "coupe")
- **brand** - Car brand
- **model** - Car model
- **color** - Car color
- **powertrain.transmission** - Transmission type
- **powertrain.fuels** - Array of fuel types
- **condition.mileage.unit** - "km" or "mil" (Swedish mile = 10km)
- **condition.mileage.value** - Mileage number
- **equipments** - Array of equipment (can be empty)

Optional fields:
- **model_type** - Model variant
- **registration_number** - Swedish registration number
- **warranty** - Warranty info (max 500 chars)
- **service_history** - Service history info (max 500 chars)
- **home_delivery** - Home delivery info (max 500 chars)
- **return_policy** - Return policy info (max 500 chars)

**Valid values:** Use the [Vehicle Reference Data](#vehicle-reference-data) endpoints to pull possible brands, models, body types, colors, fuels, transmissions, and equipment. Use these values in `category_fields` to ensure ads are accepted.

## API Endpoints

### 1. Create a New Ad

**Endpoint:** `POST /pro-import-api/v3/ad`

Creates a new advertisement. The ad will be queued for processing, and you can track its status via log messages.

**Request Headers:**
```http
POST /pro-import-api/v3/ad HTTP/1.1
Host: api-v2.tradera.com
Content-Type: application/json
X-Auth-Token: your_jwt_token_here
```

**Request Body Example (Car):**
```json
{
  "source_id": "my-car-12345",
  "dealer_code": "DEALER123",
  "category_id": 1020,
  "visible": true,
  "title": "Volvo XC90 D5 AWD",
  "body": "Well maintained Volvo XC90 with full service history. Great family car with excellent safety features.",
  "price": [
    {
      "currency": "SEK",
      "type": "list",
      "amount": 350000
    }
  ],
  "image_urls": [
    "https://example.com/images/car1-front.jpg",
    "https://example.com/images/car1-side.jpg",
    "https://example.com/images/car1-interior.jpg"
  ],
  "category_fields": {
    "brand": "volvo",
    "model": "xc90",
    "model_type": "D5 AWD",
    "model_year": 2019,
    "body_type": "suv",
    "registration_number": "ABC123",
    "color": "black",
    "condition": {
      "mileage": {
        "unit": "km",
        "value": 45000
      }
    },
    "powertrain": {
      "transmission": "automatic",
      "fuels": ["diesel"]
    },
    "equipments": [
      "navigation",
      "leather_seats",
      "parking_sensors",
      "heated_seats"
    ],
    "warranty": "12 months warranty included",
    "service_history": "Complete service history available",
    "home_delivery": "Home delivery available within 100km"
  },
  "url": "https://yourwebsite.com/cars/my-car-12345",
  "dont_publish_on_failed_images": false
}
```

**Response (201 Created):**
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "source_id": "my-car-12345",
  "dealer_code": "DEALER123",
  "state": "created",
  "created": "2026-02-17T10:30:00",
  "updated": "2026-02-17T10:30:00",
  "blocket_ad_id": null,
  "offer_id": null,
  "category_id": 1020,
  "visible": true,
  "title": "Volvo XC90 D5 AWD",
  "body": "Well maintained Volvo XC90 with full service history...",
  "price": [
    {
      "currency": "SEK",
      "type": "list",
      "amount": 350000
    }
  ],
  "image_urls": [
    "https://example.com/images/car1-front.jpg",
    "https://example.com/images/car1-side.jpg",
    "https://example.com/images/car1-interior.jpg"
  ],
  "image_refs": [],
  "category_fields": { 
    "brand": "volvo",
    "model": "xc90",
    "model_type": "D5 AWD",
    "model_year": 2019,
    "body_type": "suv",
    "color": "black",
    "condition": {
      "mileage": {
        "unit": "km",
        "value": 45000
      }
    },
    "powertrain": {
      "transmission": "automatic",
      "fuels": ["diesel"]
    },
    "equipments": ["navigation", "leather_seats", "parking_sensors", "heated_seats"]
  },
  "log": [
    {
      "action": "create",
      "state": "processing",
      "created": "2026-02-17T10:30:00",
      "request_id": "abc123-def456-ghi789",
      "external_message": null
    }
  ]
}
```

**Important Notes:**
- Image URLs are required when creating an ad
- The `source_id` must be unique - you'll get a 400 error if it already exists
- Processing is asynchronous - check the `log` field to track progress
- You cannot change the `dealer_code` after creation

### 2. Retrieve an Ad

**Endpoint:** `GET /pro-import-api/v3/ad/{source_id}`

Retrieves a single advertisement by its source_id.

**Request Example:**
```http
GET /pro-import-api/v3/ad/my-car-12345 HTTP/1.1
Host: api-v2.tradera.com
X-Auth-Token: your_jwt_token_here
```

**Response (200 OK):**
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "source_id": "my-car-12345",
  "dealer_code": "DEALER123",
  "state": "created",
  "blocket_ad_id": "98765432",
  "offer_id": null,
  "created": "2026-02-17T10:30:00",
  "updated": "2026-02-17T10:30:00",
  "category_id": 1020,
  "visible": true,
  "title": "Volvo XC90 D5 AWD",
  "body": "Well maintained Volvo XC90...",
  "price": [...],
  "image_urls": [...],
  "image_refs": [...],
  "category_fields": {...},
  "log": [
    {
      "action": "create",
      "state": "done",
      "created": "2026-02-17T10:30:15",
      "request_id": "abc123-def456-ghi789",
      "external_message": null
    },
    {
      "action": "handle_media",
      "state": "done",
      "created": "2026-02-17T10:30:20",
      "request_id": "abc123-def456-ghi789",
      "external_message": null
    }
  ]
}
```

**Error Response (404 Not Found):**
If the ad doesn't exist or you don't have access to it.

### 3. List All Ads

**Endpoint:** `GET /pro-import-api/v3/ad`

Retrieves a paginated list of all ads belonging to your dealer code(s).

**Query Parameters:**
- `limit` - Number of results per page (max 50, default 50)
- `offset` - Starting position for pagination (default 0)
- `dealer_code` - Filter by specific dealer code
- `registration_number` - Filter by registration number
- `source_id` - Filter by exact source ID
- `source_id__icontains` - Filter by source ID containing text
- `source_id__in` - Filter by multiple source IDs (comma-separated)
- `blocket_ad_id` - Filter by Blocket ad ID
- `state` - Filter by state (`created` or `deleted`)
- `created` - Filter by exact creation date
- `created__gt` - Created after date
- `created__gte` - Created at or after date
- `created__lt` - Created before date
- `created__lte` - Created at or before date
- `updated` - Filter by exact update date
- `updated__gt` - Updated after date
- `updated__gte` - Updated at or after date
- `updated__lt` - Updated before date
- `updated__lte` - Updated at or before date
- `ordering` - Order results (specify field name)

**Request Example:**
```http
GET /pro-import-api/v3/ad?limit=10&offset=0&state=created HTTP/1.1
Host: api-v2.tradera.com
X-Auth-Token: your_jwt_token_here
```

**Response (200 OK):**
```json
{
  "count": 42,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "source_id": "my-car-12345",
      "dealer_code": "DEALER123",
      "state": "created",
      "created": "2026-02-17T10:30:00",
      "updated": "2026-02-17T10:30:00",
      "blocket_ad_id": "123456",
      "category_id": 1020,
      "visible": true,
      "title": "Volvo XC90 D5 AWD",
      ...
    }
  ]
}
```

**Filter Examples:**
```http
# Get all ads updated in the last 24 hours
GET /pro-import-api/v3/ad?updated__gte=2026-02-16T00:00:00

# Get multiple ads by source_id
GET /pro-import-api/v3/ad?source_id__in=car-123,car-456,car-789

# Get ads with partial source_id match
GET /pro-import-api/v3/ad?source_id__icontains=volvo
```

### 4. Update an Ad (Full Update)

**Endpoint:** `PUT /pro-import-api/v3/ad/{source_id}`

Replaces all fields of an existing ad with new data. You must provide the complete ad object.

**Important Constraints:**
- Cannot update an ad that has been deleted
- Cannot update an ad that has not been published yet (unless create failed)
- The `source_id` in the path must match the `source_id` in the body
- Cannot change the `dealer_code`
- If the ad hasn't been published yet and create failed, PUT will retry the creation

**Request Example:**
```http
PUT /pro-import-api/v3/ad/my-car-12345 HTTP/1.1
Host: api-v2.tradera.com
Content-Type: application/json
X-Auth-Token: your_jwt_token_here
```

**Request Body:**
```json
{
  "source_id": "my-car-12345",
  "dealer_code": "DEALER123",
  "category_id": 1020,
  "visible": true,
  "title": "Volvo XC90 D5 AWD - Price Reduced!",
  "body": "Well maintained Volvo XC90 with full service history. Price reduced for quick sale!",
  "price": [
    {
      "currency": "SEK",
      "type": "list",
      "amount": 325000
    }
  ],
  "image_urls": [
    "https://example.com/images/car1-front.jpg",
    "https://example.com/images/car1-side.jpg",
    "https://example.com/images/car1-interior.jpg"
  ],
  "category_fields": {
    "brand": "volvo",
    "model": "xc90",
    "model_type": "D5 AWD",
    "model_year": 2019,
    "body_type": "suv",
    "registration_number": "ABC123",
    "color": "black",
    "condition": {
      "mileage": {
        "unit": "km",
        "value": 46000
      }
    },
    "powertrain": {
      "transmission": "automatic",
      "fuels": ["diesel"]
    },
    "equipments": ["navigation", "leather_seats"]
  }
}
```

**Response (200 OK):**
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "source_id": "my-car-12345",
  "state": "created",
  "updated": "2026-02-17T14:20:00",
  "log": [
    {
      "action": "update",
      "state": "processing",
      "created": "2026-02-17T14:20:00",
      "request_id": "xyz789-abc123-def456",
      "external_message": null
    }
  ]
}
```

**Error Responses:**
- **400 Bad Request** - `"SourceId in path must match SourceId in body."`
- **400 Bad Request** - `"Dealer code cannot be changed."`
- **400 Bad Request** - `"Cannot update ad that has been deleted."`
- **400 Bad Request** - `"Cannot update ad that has not been published yet."`
- **404 Not Found** - Ad doesn't exist or no access

### 5. Update an Ad (Partial Update)

**Endpoint:** `PATCH /pro-import-api/v3/ad/{source_id}`

Updates only the fields you specify. Other fields remain unchanged. This is more efficient than PUT when you only need to change a few fields.

**Important Constraints:**
- Cannot update an ad that has been deleted
- Cannot update an ad that has not been published yet
- The `source_id` in the path must match the `source_id` in the body
- Cannot change the `dealer_code`
- Must include both `source_id` and `dealer_code` even though they can't change

**Request Example:**
```http
PATCH /pro-import-api/v3/ad/my-car-12345 HTTP/1.1
Host: api-v2.tradera.com
Content-Type: application/json
X-Auth-Token: your_jwt_token_here
```

**Request Body (update price only):**
```json
{
  "source_id": "my-car-12345",
  "dealer_code": "DEALER123",
  "price": [
    {
      "currency": "SEK",
      "type": "list",
      "amount": 315000
    }
  ]
}
```

**Request Body (update price and visibility):**
```json
{
  "source_id": "my-car-12345",
  "dealer_code": "DEALER123",
  "visible": false,
  "price": [
    {
      "currency": "SEK",
      "type": "list",
      "amount": 299000
    }
  ]
}
```

**Request Body (update category fields):**
```json
{
  "source_id": "my-car-12345",
  "dealer_code": "DEALER123",
  "category_fields": {
    "condition": {
      "mileage": {
        "unit": "km",
        "value": 47000
      }
    }
  }
}
```

**Response (200 OK):**
Returns the complete ad object with all fields, including the updated ones, plus a new log entry showing the update action in "processing" state.

**Error Responses:**
- **400 Bad Request** - `"SourceId in path must match SourceId in body."`
- **400 Bad Request** - `"Dealer code cannot be changed."`
- **400 Bad Request** - `"Cannot update ad that has been deleted."`
- **400 Bad Request** - `"Cannot update ad that has not been published yet."`
- **404 Not Found** - Ad doesn't exist or no access

### 6. Delete an Ad

**Endpoint:** `DELETE /pro-import-api/v3/ad/{source_id}`

Marks an ad as deleted and removes it from marketplaces. Deletion is processed asynchronously.

**Request Example:**
```http
DELETE /pro-import-api/v3/ad/my-car-12345 HTTP/1.1
Host: api-v2.tradera.com
X-Auth-Token: your_jwt_token_here
```

**Response (200 OK):**
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "source_id": "my-car-12345",
  "state": "created",
  "log": [
    {
      "action": "delete",
      "state": "processing",
      "created": "2026-02-17T15:00:00",
      "request_id": "delete-123-456",
      "external_message": null
    }
  ]
}
```

**Important Notes:**
- You cannot delete an ad that has already been deleted (400 error)
- The deletion is queued for processing - check the logs to verify completion
- The ad state remains "created" - check logs for actual deletion status
- After deletion, the ad is still retrievable but marked as deleted

**Error Responses:**
- **400 Bad Request** - `"Cannot delete ad that has already been deleted."`
- **404 Not Found** - Ad doesn't exist or no access

### 7. Bump (Renew) an Ad

**Endpoint:** `GET /pro-import-api/v3/ad/{source_id}/bump`

Renews an ad to make it appear at the top of listings on supported marketplaces.

**Query Parameters:**
- `exclude_blocket` - Don't bump on Blocket (default: false)
- `exclude_bytbil` - Don't bump on Bytbil (default: false)
- `premium` - Make ad Premium on Blocket (default: false)

**Request Example:**
```http
GET /pro-import-api/v3/ad/my-car-12345/bump?premium=true HTTP/1.1
Host: api-v2.tradera.com
X-Auth-Token: your_jwt_token_here
```

**Request Example (exclude specific marketplace):**
```http
GET /pro-import-api/v3/ad/my-car-12345/bump?exclude_bytbil=true
```

**Response (200 OK):**
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "source_id": "my-car-12345",
  "state": "created",
  "created": "2026-02-17T10:30:00",
  "updated": "2026-02-17T16:00:00",
  "log": [
    {
      "action": "bump",
      "state": "processing",
      "created": "2026-02-17T16:00:00",
      "request_id": "bump-789-012",
      "external_message": null
    }
  ]
}
```

**Important Notes:**
- Can only bump ads that have been published (have an ItemId)
- The bump action is queued for processing - check logs for completion
- Bumping may incur additional costs depending on your agreement

**Error Responses:**
- **400 Bad Request** - `"Cannot bump ad that has not been published yet."`
- **404 Not Found** - Ad doesn't exist or no access

### 8. Retrieve Ad Logs

**Endpoint:** `GET /pro-import-api/v3/ad/{source_id}/log`

Retrieves detailed log messages for an ad, showing the processing status of all actions. This is crucial for monitoring the asynchronous processing of create, update, delete, and bump operations.

**Query Parameters:**
- `action` - Filter by action type (create, update, delete, bump, handle_media, publish, unpublish)
- `state` - Filter by state (processing, done, error)
- `request_id` - Filter by specific request ID (UUID)
- `created` - Filter by exact creation date
- `created__gt` - Created after date
- `created__gte` - Created at or after date
- `created__lt` - Created before date
- `created__lte` - Created at or before date
- `limit` - Number of results per page (max 50, default 50)
- `offset` - Starting position for pagination
- `ordering` - Order results by field

**Request Example:**
```http
GET /pro-import-api/v3/ad/my-car-12345/log?action=create HTTP/1.1
Host: api-v2.tradera.com
X-Auth-Token: your_jwt_token_here
```

**Request Example (check for errors):**
```http
GET /pro-import-api/v3/ad/my-car-12345/log?state=error
```

**Response (200 OK):**
```json
{
  "count": 3,
  "next": null,
  "previous": null,
  "results": [
    {
      "action": "create",
      "state": "done",
      "created": "2026-02-17T10:30:00",
      "request_id": "abc123-def456-ghi789",
      "external_message": null
    },
    {
      "action": "handle_media",
      "state": "done",
      "created": "2026-02-17T10:30:30",
      "request_id": "abc123-def456-ghi789",
      "external_message": "Images processed successfully"
    }
  ]
}
```

**Response (Error Log Example):**
```json
{
  "count": 1,
  "results": [
    {
      "action": "handle_media",
      "state": "error",
      "created": "2026-02-17T10:30:30",
      "request_id": "abc123-def456-ghi789",
      "external_message": "Failed to download image from URL: https://example.com/missing.jpg"
    }
  ]
}
```

**Log Action Types:**
- **create** - Ad creation process
- **update** - Ad update process
- **delete** - Ad deletion process
- **bump** - Ad bump/renewal process
- **handle_media** - Image processing
- **publish** - Publishing to marketplaces
- **unpublish** - Unpublishing from marketplaces

**Log States:**
- **processing** - Action in progress
- **done** - Action completed successfully
- **error** - Action failed (check external_message for details)

### 9. Retry Processing (Not Supported)

**Endpoint:** `GET /pro-import-api/v3/ad/{source_id}/retry`

This endpoint exists but is **not supported** by Tradera. It will always return an error.

**Request Example:**
```http
GET /pro-import-api/v3/ad/my-car-12345/retry HTTP/1.1
Host: api-v2.tradera.com
X-Auth-Token: your_jwt_token_here
```

**Response (200 OK):**
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "source_id": "my-car-12345",
  "log": [
    {
      "action": "retry",
      "state": "error",
      "created": "2026-02-17T16:00:00",
      "external_message": "Not supported by Tradera",
      "request_id": null
    }
  ]
}
```

**Note:** Do not use this endpoint. If you need to retry a failed operation, use PUT to update the ad instead.

---

### 10. Validate Ad Data

**Endpoint:** `POST /pro-import-api/v3/ad/validate`

Validates ad data without creating an ad. Useful for testing your data structure before submitting real ads.

**Request Example:**
```http
POST /pro-import-api/v3/ad/validate HTTP/1.1
Host: api-v2.tradera.com
Content-Type: application/json
X-Auth-Token: your_jwt_token_here
```

**Request Body:**
```json
{
  "source_id": "test-validation",
  "dealer_code": "DEALER123",
  "category_id": 1020,
  "body": "Test ad description",
  "price": [
    {
      "currency": "SEK",
      "type": "list",
      "amount": 299000
    }
  ],
  "category_fields": {
    "brand": "volvo",
    "model": "xc90",
    "model_year": 2019,
    "body_type": "suv",
    "color": "black",
    "condition": {
      "mileage": {
        "unit": "km",
        "value": 50000
      }
    },
    "powertrain": {
      "transmission": "automatic",
      "fuels": ["diesel"]
    },
    "equipments": []
  }
}
```

**Response (200 OK):**
Returns the validated data with system-generated fields (id, created, updated, etc.) set to defaults:
```json
{
  "id": "00000000-0000-0000-0000-000000000000",
  "source_id": "test-validation",
  "dealer_code": "DEALER123",
  "state": "created",
  "created": "2026-02-17T16:00:00",
  "updated": "2026-02-17T16:00:00",
  "blocket_ad_id": null,
  "image_refs": [],
  "log": [],
  "category_id": 1020,
  "body": "Test ad description",
  "price": [...],
  "category_fields": {...}
}
```

**Response (400 Bad Request):**
Returns validation errors in the same format as create/update errors:
```json
{
  "category_fields.brand": [
    "This field is required."
  ],
  "price": [
    "This field is required."
  ]
}
```

**Notes:**
- No ad is actually created - this is purely for validation
- Still requires valid dealer_code access
- Use this to test your data structure before production

## Vehicle Reference Data

When creating or updating car ads, you need to use valid values for `brand`, `model`, `body_type`, `color`, `powertrain.transmission`, `powertrain.fuels`, and `equipments`. You can fetch the allowed values from these endpoints:

| Endpoint | Description |
|----------|-------------|
| `GET /vehicle/brands` | Available car brands |
| `GET /vehicle/car-models` | Available car models |
| `GET /vehicle/car-body-styles` | Body types (e.g. suv, sedan, coupe) |
| `GET /vehicle/colors` | Available colors |
| `GET /vehicle/fuels` | Fuel types |
| `GET /vehicle/transmissions` | Transmission types |
| `GET /vehicle/equipments` | Equipment options |

**Request Example:**
```http
GET /vehicle/brands HTTP/1.1
Host: api-v2.tradera.com
X-Auth-Token: your_jwt_token_here
```

**Response (200 OK):**
```json
["volvo", "bmw", "mercedes", "audi", ...]
```

All endpoints return a JSON array of strings. Use these values exactly as returned in your `category_fields` when creating or updating ads. Using values not in these lists may cause validation errors.

## Example Workflows

### Workflow 1: Creating Your First Ad

```bash
# Step 1: Validate your data first
curl -X POST "https://api-v2.tradera.com/pro-import-api/v3/ad/validate" \
  -H "X-Auth-Token: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d @new-car-data.json

# Step 2: Create the ad
curl -X POST "https://api-v2.tradera.com/pro-import-api/v3/ad" \
  -H "X-Auth-Token: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d @new-car-data.json

# Step 3: Check the processing status (wait a few seconds first)
sleep 5
curl -X GET "https://api-v2.tradera.com/pro-import-api/v3/ad/my-car-12345/log" \
  -H "X-Auth-Token: YOUR_TOKEN"

# Step 4: Verify the ad was created successfully
curl -X GET "https://api-v2.tradera.com/pro-import-api/v3/ad/my-car-12345" \
  -H "X-Auth-Token: YOUR_TOKEN"
```

### Workflow 2: Updating Price

```bash
# Use PATCH to update only the price
curl -X PATCH "https://api-v2.tradera.com/pro-import-api/v3/ad/my-car-12345" \
  -H "X-Auth-Token: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "source_id": "my-car-12345",
    "dealer_code": "DEALER123",
    "price": [
      {
        "currency": "SEK",
        "type": "list",
        "amount": 295000
      }
    ]
  }'
```

### Workflow 3: Monitoring Ad Status

```bash
# List all ads with recent updates (last 24 hours)
curl -X GET "https://api-v2.tradera.com/pro-import-api/v3/ad?updated__gte=2026-02-16T00:00:00&limit=20" \
  -H "X-Auth-Token: YOUR_TOKEN"

# Check for any errors in processing for a specific ad
curl -X GET "https://api-v2.tradera.com/pro-import-api/v3/ad/my-car-12345/log?state=error" \
  -H "X-Auth-Token: YOUR_TOKEN"

# Check all logs for a specific request
curl -X GET "https://api-v2.tradera.com/pro-import-api/v3/ad/my-car-12345/log?request_id=abc123-def456" \
  -H "X-Auth-Token: YOUR_TOKEN"
```

### Workflow 4: Bulk Operations

```bash
# Retrieve all active ads
curl -X GET "https://api-v2.tradera.com/pro-import-api/v3/ad?state=created&limit=50" \
  -H "X-Auth-Token: YOUR_TOKEN" \
  > active-ads.json

# Process each ad (example: bump all ads)
# Note: Implement rate limiting in your script
for source_id in $(jq -r '.results[].source_id' active-ads.json); do
  curl -X GET "https://api-v2.tradera.com/pro-import-api/v3/ad/${source_id}/bump" \
    -H "X-Auth-Token: YOUR_TOKEN"
  sleep 1  # Rate limiting
done
```

### Workflow 5: Handling Failed Ad Creation

```bash
# Check if ad creation failed
LOGS=$(curl -s -X GET "https://api-v2.tradera.com/pro-import-api/v3/ad/my-car-12345/log?action=create" \
  -H "X-Auth-Token: YOUR_TOKEN")

# If create failed, you can retry using PUT
curl -X PUT "https://api-v2.tradera.com/pro-import-api/v3/ad/my-car-12345" \
  -H "X-Auth-Token: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d @new-car-data.json
```

## Error Handling

### HTTP Status Codes

- `200 OK` - Request successful (GET, PUT, PATCH, DELETE)
- `201 Created` - Ad created successfully (POST)
- `400 Bad Request` - Validation error or invalid request
- `403 Forbidden` - Authentication failed or insufficient dealer code permissions
- `404 Not Found` - Ad not found or no access

### Common Error Responses

**Validation Error (400):**

Field validation errors return a JSON object with field names as keys and error messages as arrays:

```json
{
  "category_fields.brand": [
    "This field is required."
  ],
  "category_fields.powertrain.fuels": [
    "This field is required."
  ],
  "price": [
    "This field is required."
  ]
}
```

**Dealer Code Access Error (403):**
```json
{
  "dealer_code": [
    "You do not have access to dealer code 'WRONG_CODE'. Allowed dealer codes: DEALER123, DEALER456."
  ]
}
```

**No Dealer Codes (403):**
```json
{
  "dealer_code": [
    "You do not have access to dealer code 'DEALER123'. You have no dealer codes assigned."
  ]
}
```

**Business Logic Errors (400):**

These are returned as plain text strings:

```
"ImageUrls are required"
"A PublicAd with SourceId 'my-car-12345' already exists."
"SourceId in path must match SourceId in body."
"Dealer code cannot be changed."
"Cannot update ad that has been deleted."
"Cannot update ad that has not been published yet."
"Cannot delete ad that has already been deleted."
"Cannot bump ad that has not been published yet."
```

### Processing Errors

Even if the API returns 201 Created or 200 OK, the actual processing happens asynchronously. Always check the logs for errors:

```bash
curl -X GET "https://api-v2.tradera.com/pro-import-api/v3/ad/my-car-12345/log?state=error" \
  -H "X-Auth-Token: YOUR_TOKEN"
```

Example error log entry:
```json
{
  "action": "handle_media",
  "state": "error",
  "created": "2026-02-17T10:31:00",
  "request_id": "abc123-def456-ghi789",
  "external_message": "Failed to download image from URL"
}
```

### Debugging Tips

1. **Check if the ad was created:** A 201 response doesn't guarantee the ad is published
2. **Wait before checking logs:** Processing takes a few seconds - wait 5-10 seconds before checking
3. **Filter logs by request_id:** Use the request_id from the response to track all related actions
4. **Look for "done" state:** Only when action state is "done" can you be sure it succeeded
5. **Image errors:** These show up in "handle_media" actions - ensure all image URLs are accessible

## Best Practices

### 1. Use Unique Source IDs

Always use unique, meaningful `source_id` values that match your internal system's IDs. The source_id must be unique - duplicate source_ids will result in a 400 error.

```json
{
  "source_id": "inventory-ABC123-2024"
}
```

### 2. Validate Before Creating

Always validate your ad data using the `/pro-import-api/v3/ad/validate` endpoint before creating ads in production. This helps catch validation errors early.

```bash
curl -X POST "https://api-v2.tradera.com/pro-import-api/v3/ad/validate" \
  -H "X-Auth-Token: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d @ad-data.json
```

### 3. Monitor Logs for Async Processing

The API returns immediately, but processing happens asynchronously. Always monitor logs:

```bash
# Wait a few seconds after creation, then check logs
sleep 5
curl -X GET "https://api-v2.tradera.com/pro-import-api/v3/ad/my-car-12345/log?state=error" \
  -H "X-Auth-Token: YOUR_TOKEN"
```

### 4. Handle Image URLs Properly

- **Required on create:** Image URLs are required when creating an ad
- Ensure all image URLs are publicly accessible (no authentication required)
- Use HTTPS URLs when possible
- Provide images in the order you want them displayed
- The images will be downloaded and processed asynchronously
- Check `handle_media` logs to verify successful image processing

### 5. Use PATCH for Simple Updates

For price or visibility changes, use `PATCH` instead of `PUT`:

```json
{
  "source_id": "my-car-12345",
  "dealer_code": "DEALER123",
  "visible": false,
  "price": [{"currency": "SEK", "type": "list", "amount": 299000}]
}
```

### 6. Understand Update Restrictions

- Cannot update ads that haven't been published yet (unless creation failed)
- Cannot update deleted ads
- Cannot change the dealer_code
- source_id in path must match source_id in body
- Use PUT on failed ads to retry creation

### 7. Include Required Fields

Required for all ads:
- `source_id`, `dealer_code`, `category_id` (1020 or 1021)
- `body`, `price` (array with at least one price object)
- `category_fields` with all required car fields
- `image_urls` (required on create)

### 8. Handle Request IDs for Tracking

Each operation returns a `request_id` that groups all related log entries:

```python
response = create_ad(ad_data)
request_id = response['log'][0]['request_id']

# Later, track all actions for this request
logs = get_logs(source_id, request_id=request_id)
```

### 9. Implement Proper Error Handling

```python
try:
    response = create_ad(ad_data)
    source_id = response['source_id']
    
    # Wait for processing
    time.sleep(5)
    
    # Check for errors
    logs = get_logs(source_id, state='error')
    if logs['count'] > 0:
        print(f"Error: {logs['results'][0]['external_message']}")
except HTTPError as e:
    if e.response.status_code == 400:
        # Validation error
        errors = e.response.json()
        for field, messages in errors.items():
            print(f"{field}: {', '.join(messages)}")
    elif e.response.status_code == 403:
        print("Access denied - check dealer_code")
```

### 10. Price Format

Price must be an array with currency "SEK" and type "list":

```json
{
  "price": [
    {
      "currency": "SEK",
      "type": "list",
      "amount": 350000
    }
  ]
}
```

### 11. Category ID is Numeric

The `category_id` field is numeric, not a string:

```json
{
  "category_id": 1020
}
```

### 12. Don't Use the Retry Endpoint

The `/retry` endpoint is not supported and always returns an error. If you need to retry a failed operation, use PUT to update the ad instead.

---

## Support

For additional help or API access, please contact Tradera API support.

This guide is based on the actual ProImportController implementation in the tradera-api repository.
