This flow uploads photos, creates a test order, and retrieves it.
1. Get a Test API key
Create or copy a Test key in
API key settings with
orders:write and orders:read.
Live and Test keys use the same base URL:
https://api.legitgrails.com/v1/integrations
Send the key as Authorization: Bearer YOUR_TEST_API_KEY in each
request.
2. Resolve catalog codes
List categories, then find a compatible brand, available answer time, and photo
requirements. This example uses Gucci bags:
curl --fail-with-body --silent --show-error \
"https://api.legitgrails.com/v1/integrations/catalog/categories?locale=en" \
--header "Authorization: Bearer YOUR_TEST_API_KEY"
curl --fail-with-body --silent --show-error \
"https://api.legitgrails.com/v1/integrations/catalog/brands?category_code=bag&search=gucci" \
--header "Authorization: Bearer YOUR_TEST_API_KEY"
curl --fail-with-body --silent --show-error \
"https://api.legitgrails.com/v1/integrations/catalog/answer_times?brand_code=gucci&category_code=bag" \
--header "Authorization: Bearer YOUR_TEST_API_KEY"
curl --fail-with-body --silent --show-error \
"https://api.legitgrails.com/v1/integrations/catalog/photo_indexes?brand_code=gucci&category_code=bag" \
--header "Authorization: Bearer YOUR_TEST_API_KEY"
Catalog arrays are returned in data. Use their exact code values, select an
answer time with available: true, and provide each required photo without
exceeding its photo_limit. See Catalog for models and other query
options.
3. Upload photos
Upload the photos required by the selected photo indexes. Send each file as a
photos part to POST /photos:
curl --fail-with-body --silent --show-error \
--request POST "https://api.legitgrails.com/v1/integrations/photos" \
--header "Authorization: Bearer YOUR_TEST_API_KEY" \
--form "photos=@./overall.jpg" \
--form "photos=@./serial-number.jpg"
The HTTP 200 response contains one result per file. Use the URL only when
status is uploaded:
[
{
"status" : "uploaded" ,
"url" : "https://media.legitgrails.com/uploads/tusd/example-overall" ,
"original_name" : "overall.jpg"
},
{
"status" : "uploaded" ,
"url" : "https://media.legitgrails.com/uploads/tusd/example-serial" ,
"original_name" : "serial-number.jpg"
}
]
An individual result can instead have status: "failed", an error, and no
url. Retry or replace that file before creating the order. Use each successful
URL with the corresponding catalog index_code.
4. Create an order
curl --fail-with-body --silent --show-error \
--request POST "https://api.legitgrails.com/v1/integrations/orders" \
--header "Authorization: Bearer YOUR_TEST_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"brand_code": "gucci",
"category_code": "bag",
"answer_time": 720,
"mock_outcome": "authentic",
"photos": [
{
"url": "https://media.legitgrails.com/uploads/tusd/example-overall",
"index_code": "overall-picture"
},
{
"url": "https://media.legitgrails.com/uploads/tusd/example-serial",
"index_code": "serial-number"
}
]
}'
A successful request returns a response like this:
{
"id" : "<order-id>" ,
"status" : "completed" ,
"due_date" : "2026-08-04T22:30:00.000Z"
}
Persist id; you will use it to retrieve the order and submit replacement
photos if requested.
5. Retrieve the order
Use the returned order ID:
curl --fail-with-body --silent --show-error \
"https://api.legitgrails.com/v1/integrations/orders/<order-id>?locale=en" \
--header "Authorization: Bearer YOUR_TEST_API_KEY"
{
"id" : "<order-id>" ,
"status" : "completed" ,
"outcome" : "authentic" ,
"due_date" : "2026-08-04T22:30:00.000Z" ,
"updated_at" : "2026-08-04T10:30:04.000Z"
}
Drive your workflow from status and outcome. See Orders for every
lifecycle response and Photos for the update-photos flow.
Move to Live
For a live order, keep the same base URL, remove mock_outcome, and replace the
Test API key with a Live API key that has the required scopes. The key selects
the environment.
Poll GET /orders/{id} or use webhooks for completed and
photo-update notifications.
Continue with Integration setup and the
API Reference .
Last modified on September 4, 2026