Transform a vehicle
Send raw images and/or video for one vehicle and get studio-grade outputs.
Send raw images and/or video for one vehicle. Spyne returns studio-grade images, a 360 spin and a feature video from the same request.
Submits vehicle images or video for asynchronous processing into catalog images, a 360 spin, a feature video, or media kit assets.
Prefer ReadMe's own explorer? Open it here.
Before you call it
Provide at least one vehicle identifier. Any one of the following is sufficient:
- vin: the vehicle's 17-character alphanumeric VIN
- stockNumber: the dealer's internal inventory identifier
- registrationNumber: the vehicle's official registration number
Processing is asynchronous. A 202 response confirms only that the request was accepted and returns a dealerVinID. Retrieve the output later with the Retrieve Media endpoint, or receive it by webhook.
Image URLs are validated before processing begins. A maximum of 200 URLs is accepted per request, each must use the http or https scheme, and each must return a 2xx or 3xx status when fetched. Duplicate URLs are collapsed.
Request body
- Identify the vehicle: send at least one of
vin,stockNumberorregistrationNumber. - Pick an output: in
media, set at least one ofimage,spinorfeatureVideototrue.mediaKitis optional. - Send the files: in
mediaInput, sendimageData,videoDataor both. - Configure each output you picked:
processingDetails.backgroundIdis always required.media.imageneedsprocessingDetails.image,media.spinneedsprocessingDetails.spinandmedia.featureVideoneedsprocessingDetails.featureVideo.
| Parameter | Type | Required | Description |
|---|---|---|---|
| Vehicle identifier | — | Required | Send at least one of the three fields below. Use vin when you have it. |
vin | string | At least one | The unique 17-character VIN of the vehicle. |
stockNumber | string | At least one | A dealer's internal identifier for inventory tracking |
registrationNumber | string | At least one | The vehicle's official registration number used for identification |
media | object | Required | The outputs to generate. Set at least one of image, spin or featureVideo to true. mediaKit is optional and can be added to any of them. |
media.image | boolean | At least one | Set to "true" to process the vehicle images into a studio-ready format |
media.spin | boolean | At least one | Set to "true" to create a 360° spin for the vehicle |
media.featureVideo | boolean | At least one | Set to "true" to generate a feature video highlighting key vehicle details |
media.mediaKit | boolean | Optional | Set to "true" to generate media kit assets for the vehicle |
mediaInput | object | Required | The source files to process. Send imageData, videoData or both. |
mediaInput.imageData | object[] | At least one | An array of URLs pointing to the images to be processed for studio-ready outputs. |
mediaInput.imageData[].url | string | Required | URL of the image. Required in every entry; an entry without a URL is ignored. |
mediaInput.imageData[].clientMetaData | object | Optional | Opaque per-image metadata. Stored against the asset and echoed back unchanged on Retrieve Media. Spyne does not interpret these keys. |
mediaInput.videoData | object[] | At least one | An array of URLs pointing to videos, primarily used for generating 360° spins or feature videos. Only "url" is read; any other keys are ignored. |
mediaInput.videoData[].url | string | Required | Publicly reachable source video URL. Required in every entry; an entry without a URL is ignored. |
processingDetails | object | Required | Settings for the outputs you selected in media. backgroundId is always required, and each selected output needs its own settings object. |
processingDetails.numberPlateLogo | string | Optional | It specifies the treatment for the vehicle’s number plate. You can also Pass "0" for no number plate masking, "1" for a plain white masking, or a "URL" to use a custom number plate masking |
processingDetails.backgroundId | string | Required | Identifier for the background to composite behind the vehicle. This ID is created by the Spyne team. Required for every request: without it the request comes back as a 206 carrying error code "VAL_CONFIG_001". Example: 75282. |
processingDetails.image | object | Required if | Configurations to generate a catalog-ready images of the vehicle. Required when media.image is true. If it is missing, that output is skipped and the response is a 206 with error code "VAL_CONFIG_004". Send an empty object to use the defaults. |
processingDetails.image.catalogCount | integer | Optional | The number of catalog images to generate. Defaults to 8 when omitted. "Note": this is only needed when a video input is provided; for image input it can be left blank. Default: 8. |
processingDetails.spin | object | Required if | Configurations to generate a 360-spin of the vehicle. Required when media.spin is true. If it is missing, that output is skipped and the response is a 206 with error code "VAL_CONFIG_004". Send an empty object to use the defaults. |
processingDetails.spin.hotspot | boolean | Optional | Set to "true" to detect and include interactive hotspots on the spin. Defaults to false when omitted. Default: false. |
processingDetails.spin.spinFrameCount | string (one of: 36, 72) | Optional | Number of frames to be used in the 360° spin generation. The accepted values are "36" or "72". Default: 36. |
processingDetails.featureVideo | object | Required if | Configurations to generate a feature Video. Required when media.featureVideo is true. If it is missing, that output is skipped and the response is a 206 with error code "VAL_CONFIG_004". Send an empty object to use the defaults. |
processingDetails.featureVideo.templateId | string | Optional | Identifier for the video template used to generate the feature video. This ID is provided by the Spyne team; there is no default. |
processingDetails.featureVideo.musicId | string | Optional | Identifier for the background music to include in the video. This ID is provided by the Spyne team; there is no default. |
processingDetails.featureVideo.voiceId | string | Optional | Identifier for the voice-over added to the video. This ID is provided by the Spyne team; there is no default. |
processingDetails.featureVideo.language | string | Optional | Language used for the generated voice-over script. Defaults to "english" when omitted. Default: english. |
processingDetails.featureVideo.targetLanguage | string | Optional | Output language code for the voice-over. Defaults to "en" when omitted. Default: en. |
processingDetails.featureVideo.font | object | Optional | Font used for on-screen text, with "text" and "key" properties. Defaults to Montserrat for both when omitted. |
processingDetails.featureVideo.font.text | string | Optional | Example: Montserrat. |
processingDetails.featureVideo.font.key | string | Optional | Example: Montserrat. |
processingDetails.featureVideo.themeColors | object | Optional | Theme colours applied to the video, with a "primaryColor" property. Defaults to "#ffca13" when omitted. |
processingDetails.featureVideo.themeColors.primaryColor | string | Optional | Example: #ffca13. |
dealerId | string | Optional | Deprecated and ignored. This field is accepted for backward compatibility but has no effect on processing. The team the vehicle is processed under is always taken from the authenticated API credential, not from this value. |
vinMetaData | object | Optional | Free-form vehicle attributes such as make, model, year, engine, interior and exterior details. Stored against the vehicle record and not interpreted by processing. |
clientMetaData | object | Optional | Opaque request-level metadata. Stored against the record and echoed back on Retrieve Media. Spyne does not interpret these keys. |
mediaKitConfig | object | Optional | Media kit overrides. "Note": supplying "banner" or "billboard" is rejected with 400 if the enterprise and team have a smart campaign in ACTIVE, PAUSED or SCHEDULED state, because a live campaign would override the media kit sent here. |
mediaKitConfig.banner | object | Optional | |
mediaKitConfig.billboard | object | Optional |
Sample request
curl --request POST \
--url 'https://api.spyne.ai/api/pv1/merchandise/process' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"vin": "1HGCM82633A004352",
"vinMetaData": {
"make": "Honda",
"model": "Accord",
"year": "2020"
},
"clientMetaData": {
"dmsRef": "RO-88213"
},
"media": {
"image": true,
"spin": true
},
"mediaInput": {
"imageData": [
{
"url": "https://cdn.example.com/veh/front.jpg",
"clientMetaData": {
"position": "front"
}
},
{
"url": "https://cdn.example.com/veh/rear.jpg"
}
]
},
"processingDetails": {
"backgroundId": "75282",
"numberPlateLogo": "1",
"image": {
"catalogCount": 12
},
"spin": {
"hotspot": true,
"spinFrameCount": "36"
}
}
}'import requests
url = "https://api.spyne.ai/api/pv1/merchandise/process"
headers = {"Authorization": "Bearer YOUR_API_KEY"}
payload = {
"vin": "1HGCM82633A004352",
"vinMetaData": {
"make": "Honda",
"model": "Accord",
"year": "2020"
},
"clientMetaData": {
"dmsRef": "RO-88213"
},
"media": {
"image": True,
"spin": True
},
"mediaInput": {
"imageData": [
{
"url": "https://cdn.example.com/veh/front.jpg",
"clientMetaData": {
"position": "front"
}
},
{
"url": "https://cdn.example.com/veh/rear.jpg"
}
]
},
"processingDetails": {
"backgroundId": "75282",
"numberPlateLogo": "1",
"image": {
"catalogCount": 12
},
"spin": {
"hotspot": True,
"spinFrameCount": "36"
}
}
}
response = requests.post(url, headers=headers, json=payload)
print(response.status_code, response.json())const response = await fetch("https://api.spyne.ai/api/pv1/merchandise/process", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
"vin": "1HGCM82633A004352",
"vinMetaData": {
"make": "Honda",
"model": "Accord",
"year": "2020"
},
"clientMetaData": {
"dmsRef": "RO-88213"
},
"media": {
"image": true,
"spin": true
},
"mediaInput": {
"imageData": [
{
"url": "https://cdn.example.com/veh/front.jpg",
"clientMetaData": {
"position": "front"
}
},
{
"url": "https://cdn.example.com/veh/rear.jpg"
}
]
},
"processingDetails": {
"backgroundId": "75282",
"numberPlateLogo": "1",
"image": {
"catalogCount": 12
},
"spin": {
"hotspot": true,
"spinFrameCount": "36"
}
}
}),
});
console.log(response.status, await response.json());Sample response
{
"success": true,
"statusCode": 202,
"message": "Processing initiated successfully",
"data": {
"vin": "1HGCM82633A004352",
"stockNumber": "STK-4471",
"dealerVinID": "fe89aa8d-5419-41f0-99bb-1e6e45691dcb",
"source": "API",
"dealerId": "86fc402add"
}
}{
"success": true,
"statusCode": 206,
"message": "Request initiated with partial success",
"data": {
"stockNumber": "STK-4471",
"dealerVinID": "fe89aa8d-5419-41f0-99bb-1e6e45691dcb",
"source": "API",
"dealerId": "86fc402add",
"validationSummary": {
"isRequestRejected": true,
"displayError": {
"code": "VAL_CONFIG_001",
"message": "BackgroundId should not be null or empty",
"product": null
}
}
}
}{
"success": false,
"statusCode": 400,
"message": "At least one idenytifier (VIN, stockId, or registrationNumber) is required",
"error": "Bad request"
}{
"success": false,
"statusCode": 400,
"message": "Received invalid urls, URLS:[not-a-url]",
"error": "Bad request"
}{
"success": false,
"statusCode": 400,
"message": "Received inaccessible urls, URLS:[https://cdn.example.com/missing.jpg]",
"error": "Bad request"
}{
"success": false,
"statusCode": 400,
"message": "More than 200 urls is not allowed. Received 201 urls.",
"error": "Bad request"
}{
"success": false,
"statusCode": 400,
"message": "Processing with custom media kit (banner/billboard) is not allowed when an active/paused/scheduled smart campaign exists for this enterprise and team. Please deactivate the campaign before processing with a custom media kit.",
"error": "Bad request"
}{
"code": "E_MISSING_OR_INVALID_PARAMS",
"problems": [
"\"mediaInput\" is required, but it was not defined."
],
"message": "The server could not fulfill this request (`POST /api/pv1/merchandise/process`) due to 1 missing or invalid parameters."
}{
"status": "401",
"message": "Invalid Auth key"
}{
"success": false,
"statusCode": 500,
"message": "Failed to process media request",
"error": "Unknown error occurred"
}{
"success": false,
"statusCode": 500,
"message": "Failed to validate active campaigns",
"error": "Internal server error"
}Response parameters (202)
202 Accepted means the request passed validation and Spyne has started processing the vehicle. This is the only success status the endpoint returns. Processing is asynchronous, so no images, spin or video are ready yet: store the dealerVinID and use it to fetch the outputs once they are done. Identifier fields you did not send are left out of the response, not returned as null.
| Field | Type | Description |
|---|---|---|
success | boolean | Always true for a 202. |
statusCode | integer | Always 202, matching the HTTP status. |
message | string | Always Processing initiated successfully. |
data | object | The vehicle record Spyne created for this request. |
data.vin | string | The VIN you sent. Omitted if the request had no VIN. Example: 1HGCM82633A004352. |
data.stockNumber | string | The stock number you sent. Omitted if not sent. Example: STK-4471. |
data.registrationNumber | string | The registration number you sent. Omitted if not sent. Example: KA01AB1234. |
data.dealerVinID | string | Spyne's ID for this vehicle. Pass it as the dealerVinId query parameter of Retrieve a vehicle to fetch the outputs. Example: fe89aa8d-5419-41f0-99bb-1e6e45691dcb. |
data.source | string | Always API for requests made through this endpoint. |
data.dealerId | string | Team the record was attributed to, taken from the credential. Example: 86fc402add. |
Response parameters (206)
206 Partial Content means Spyne created the vehicle record and returned a dealerVinID, but the processing service rejected the processing request, typically because a required setting is missing or invalid. success is still true, so check statusCode or data.validationSummary rather than success alone. Read data.validationSummary.displayError to see what was rejected, correct the request and send it again.
| Field | Type | Description |
|---|---|---|
success | boolean | true: the vehicle record was created even though processing was rejected. |
statusCode | integer | Always 206, matching the HTTP status. |
message | string | Summary from the processing service when it provides one; otherwise Request initiated with partial success. |
data | object | The vehicle record, plus the reason processing was rejected. |
data.vin | string | The VIN you sent. Omitted if not sent. |
data.stockNumber | string | The stock number you sent. Omitted if not sent. |
data.registrationNumber | string | The registration number you sent. Omitted if not sent. |
data.dealerVinID | string | Spyne's ID for the vehicle record that was created. Example: fe89aa8d-5419-41f0-99bb-1e6e45691dcb. |
data.source | string | Always API. |
data.dealerId | string | Team the record was attributed to, taken from the credential. Example: 86fc402add. |
data.validationSummary | object | Why the processing request was rejected. Its fields are listed below by name, relative to data.validationSummary. |
isRequestRejected | boolean | Always true in a 206. |
displayError | object | The error to fix before resending. |
displayError.code | string | Machine-readable error code. Example: VAL_CONFIG_001, returned when processingDetails.backgroundId is missing. |
displayError.message | string | Human-readable explanation of the error. Example: BackgroundId should not be null or empty. |
displayError.product | string or null | The output the error applies to, when the processing service names one; otherwise null. |
Errors
| Status | Meaning | When it happens |
|---|---|---|
400 | Bad Request | No vehicle identifier supplied; an image URL was malformed, unreachable, or over the 200-URL limit; a custom media kit was sent while a live campaign exists; or any 4xx relayed from the processing service. |
401 | Unauthorized | Missing or invalid credential. Emitted by the auth layer, and shaped differently to the other errors. |
403 | Forbidden | Returned by the edge layer for a permission failure. The application itself collapses upstream 4xx into 400. |
429 | Too Many Requests | Returned by the edge layer when a rate limit is exceeded. The application itself collapses upstream 4xx into 400. |
500 | Internal Server Error | Processing service unreachable, or the campaign check failed. |
503 | Service Unavailable | Returned by the edge layer when the service is unavailable. |
What happens next
- Store the
dealerVinId. It is returned as soon as the request is accepted. - Get the results. Poll Retrieve a vehicle, or receive them on your webhook.
- Check both statuses. Publish once
aiStatusisDONEandqcStatusisqc_done.
Updated 10 days ago
