Discover the vocabulary before filtering
Call the operations in the order brands → models → search → listing. Use a supported brand label to ask for models, and carry the selected model into search. filter-options is a separate way to explore filter vocabulary; search accepts only the fields its contract declares, and an undeclared field inside body is refused with INVALID_INPUT (400).
Page search results within the search caps
Keep page within 1–750 and page_size in 25, 50, 100 or 200. Persist an Idempotency-Key with the exact action and wrapped JSON for each intent. A timeout stops the flow: retain the request identifier and unchanged intent, without minting a new key or sending another attempt. The reservation remains held until release and is never charged late; the same key is only for checking that intent. A same-key check may report REQUEST_PENDING (409), then OUTCOME_UNKNOWN_RELEASED (504) after release; it never returns a success that arrives after the timeout. Keep the successful workflow and timeout workflow separate. Only the caller can deliberately choose a new intent.
Read one advertisement at a time
Only pass an identifier into listing after checking that a search row contains a nonempty string. ad_id is the 4–12 digit identifier a search returns; an identifier that is not 4–12 digits may fail. Store the vehicle fields your application uses, such as ad_id, brand and model, rather than copying the full advertisement.
Two scenarios in one example
The JavaScript below makes no network calls. Scenario A follows original successes from brands through listing. Independent scenario B stops at a timeout, checks the same held intent, then observes release without success or a late charge. Further cases cover the top-level requestId of an admission error, an expired result and a terminal failure. The example responses carry chargedCreditUnits "0" and fixed Idempotency-Keys; a real call uses a new key for each intent and is charged the operation's per-call price when it succeeds.
// Example workflow over the five operations; responses are built in memory, with no network calls.
// 示例流程:五个操作的响应在内存中构造,不发起网络请求。
const isRecord = value => value !== null && typeof value === 'object' && !Array.isArray(value);
const isText = value => typeof value === 'string' && value.trim().length > 0;
function requireValue(condition, message) {
if (!condition) throw new Error(message);
}
function intent(action, body, identity) {
// Persist one Idempotency-Key per unchanged intent.
// The fixed keys below belong to this example; generate a new key for each real intent.
return Object.freeze({
method: 'POST', path: `/api/v1/polovniautomobili/${action}`,
headers: Object.freeze({ 'Idempotency-Key': identity }),
json: JSON.stringify({ body }) // Every input sits under body.
});
}
function inspect(response, request) {
requireValue(isRecord(response), 'Missing gateway envelope');
if (response.ok === true) {
requireValue(isRecord(response.meta) && isText(response.meta.requestId) &&
Object.hasOwn(response, 'data'), 'Invalid success envelope');
return { kind: 'success', data: response.data }; // Each step below checks the fields it uses.
}
requireValue(response.ok === false && isRecord(response.error) &&
isText(response.error.code) && isText(response.error.message), 'Invalid failure');
const code = response.error.code;
if (!Object.hasOwn(response, 'meta')) {
requireValue(isText(response.requestId), 'Missing admission request identity');
return { kind: 'admission-error', code, requestId: response.requestId };
}
requireValue(isRecord(response.meta) && isText(response.meta.requestId), 'Invalid error metadata');
const requestId = response.meta.requestId;
if (code === 'REQUEST_PENDING') {
requireValue(['dispatching', 'uncertain'].includes(response.meta.status), 'Invalid pending state');
return { kind: 'pending', code, requestId, checkIntent: request };
}
if (code === 'OUTCOME_UNKNOWN_RELEASED' || response.meta.status === 'released') {
requireValue(response.meta.chargedCreditUnits === '0', 'Unexpected released charge');
return { kind: 'released', code, requestId }; // Stop: new intent requires caller decision.
}
if (code === 'RESULT_EXPIRED') {
return { kind: 'expired', code, requestId }; // No redispatch and no assumed refund.
}
if (response.meta.status === 'uncertain') {
return { kind: 'uncertain', code, requestId, checkIntent: request }; // Stop; never new-key retry.
}
return { kind: 'failed', code, requestId }; // Includes stored terminal errors; no auto-retry.
}
function success(data, requestId) {
return { ok: true, data, meta: { requestId, chargedCreditUnits: '0',
simulated: true, sourceAttribution: {} } };
// chargedCreditUnits is '0' in this example; a real success is charged the per-call price.
}
function requireData(result) {
requireValue(result.kind === 'success' && isRecord(result.data), 'Stop: no usable data');
return result.data;
}
// Scenario A: each original response succeeds; this is NOT timeout recovery.
const brandsIntent = intent('polovniautomobili-brands', {}, 'example-brands-cedar');
const brands = requireData(inspect(success({ brands: [{ label: 'Volkswagen' }] },
'example-request-brands'), brandsIntent));
requireValue(Array.isArray(brands.brands) && brands.brands.every(row =>
isRecord(row) && isText(row.label)), 'Unexpected brands layout');
const brand = brands.brands.find(row => row.label === 'Volkswagen')?.label;
requireValue(brand === 'Volkswagen', 'Choose a supported exact brand enum label');
const modelsIntent = intent('polovniautomobili-models', { brand, sample_pages: 4 },
'example-models-cedar'); // sample_pages: integer 1–10.
const models = requireData(inspect(success({ models: [{ model: 'example-model-cedar' }] },
'example-request-models'), modelsIntent));
requireValue(Array.isArray(models.models) && models.models.length > 0 &&
models.models.every(row => isRecord(row) && isText(row.model)), 'Unexpected models layout');
const searchIntent = intent('polovniautomobili-search', {
brand, model: [models.models[0].model], page: 2, page_size: 100
}, 'example-search-cedar'); // page 1–750; page_size 25/50/100/200.
const search = requireData(inspect(success({ cars: [{ ad_id: '9182736405' }] },
'example-request-search'), searchIntent)); // Original successful response.
requireValue(Array.isArray(search.cars) && search.cars.length > 0 &&
search.cars.every(row => isRecord(row) && isText(row.ad_id)), 'Unexpected search layout');
const listingIntent = intent('polovniautomobili-listing', { ad_id: search.cars[0].ad_id },
'example-listing-cedar'); // ad_id: 4–12 digits; this site accepts any string for ad_id at admission.
const listing = requireData(inspect(success({ car: { ad_id: '9182736405',
brand, model: models.models[0].model, seller: { name: 'example-seller',
phones: ['example-contact'], address: 'example-address',
coordinates: 'example-location' } } }, 'example-request-listing'), listingIntent));
requireValue(isRecord(listing.car) && isText(listing.car.ad_id) &&
listing.car.ad_id === search.cars[0].ad_id && isText(listing.car.brand) &&
isText(listing.car.model), 'Unexpected listing layout');
// Keep only the vehicle fields this application uses.
const stored = [{ ad_id: listing.car.ad_id, brand: listing.car.brand, model: listing.car.model }];
// Scenario B: independent timeout intent; NEVER feeds listing or the success workflow.
const timeoutIntent = intent('polovniautomobili-search', {
brand: 'Volkswagen', page: 3, page_size: 100
}, 'example-timeout-cedar');
const timedOut = inspect({ ok: false,
error: { code: 'OPERATION_TIMEOUT', message: 'The operation did not finish in time and may still have run. Do not automatically repeat this request with a new Idempotency-Key.' },
meta: { requestId: 'example-timeout-request', chargedCreditUnits: '0', status: 'uncertain' }
}, timeoutIntent); // HTTP 504: stop here; no later result will be delivered.
requireValue(timedOut.kind === 'uncertain', 'Expected an uncertain result');
const sameIntentCheck = timedOut.checkIntent; // Preserve key, route and exact JSON.
requireValue(sameIntentCheck === timeoutIntent, 'Do not replace the timeout intent');
const pending = inspect({ ok: false,
error: { code: 'REQUEST_PENDING', message: 'This request was already dispatched. Do not submit a new key to retry an uncertain operation.' },
meta: { requestId: 'example-timeout-request', chargedCreditUnits: '0', status: 'uncertain' }
}, sameIntentCheck); // HTTP 409: replay reports state and never runs the operation again.
requireValue(pending.kind === 'pending', 'Expected a pending state');
const released = inspect({ ok: false,
error: { code: 'OUTCOME_UNKNOWN_RELEASED', message: 'This request ended without a usable result. Its key cannot execute another call.' },
meta: { requestId: 'example-timeout-request', chargedCreditUnits: '0', status: 'released' }
}, sameIntentCheck); // HTTP 504 after release, not a recovered success.
requireValue(released.kind === 'released', 'Expected a released hold');
// No late charge. A new intent is never automatic; it needs a deliberate caller decision.
// Independent error cases: an admission error carries requestId at the top level and no meta.
const admission = inspect({ ok: false,
error: { code: 'INVALID_REQUEST', message: 'The request does not match the documented format.' },
requestId: 'example-admission-request'
}, timeoutIntent); // HTTP 400, no meta.
requireValue(admission.kind === 'admission-error', 'Expected an admission error');
const expired = inspect({ ok: false,
error: { code: 'RESULT_EXPIRED', message: 'The result retention period ended. This request will not be executed again.' },
meta: { requestId: 'example-request-listing', chargedCreditUnits: '0', status: 'succeeded' }
}, listingIntent); // HTTP 410: not run or charged again, and not a refund.
requireValue(expired.kind === 'expired', 'Expected an expired result');
const failed = inspect({ ok: false,
error: { code: 'REQUEST_NOT_FULFILLED', message: 'The operation reported that it did not succeed. Nothing was charged.' },
meta: { requestId: 'example-request-failure', chargedCreditUnits: '0', status: 'failure' }
}, listingIntent); // An alternative outcome for the listing request.
requireValue(failed.kind === 'failed', 'Expected a terminal failure');
console.log({ stored, failure: failed.code }); // Logs the stored vehicle fields and the failure code.
Server-side request template
This server-side template shows authentication and request identity using the first operation of this API. Choose the operation for your use case and check its current availability and price on the API page. After a timeout, resend the unchanged request with the same Idempotency-Key.
The gateway request is always POST.
Use a JSON object with the documented path, query, headers and body locations; omit unused locations. input.headers is schema-validated data, not browser request headers. Do not put credentials here. Keep body arrays, scalar values and null as documented; omission is different from null.
# Server-side only. Set ATLAS_API_KEY in your secret manager.
# Set ATLAS_API_ORIGIN to your configured deployment origin.
# Every gateway operation is called with POST.
# Only operations shown as enabled on the API page can be called.
# Generate once per intent; retain the same value on status checks.
REQUEST_ID="$(uuidgen)"
curl --request POST 'https://apigate.vercel.app/api/v1/polovniautomobili/polovniautomobili-brands' \
--header "Content-Type: application/json" \
--header "x-api-key: $ATLAS_API_KEY" \
--header "Idempotency-Key: $REQUEST_ID" \
--data '{"body":{}}'Integration checklist
- The API key is stored in a server-side secret manager and never sent to a browser.
- Each request carries its own Idempotency-Key, kept until the request has a final answer.
- The integration reads ok and error.code and handles each error code as the error table describes.
- An uncertain POST is inspected using its original identity, never blindly repeated with a new one.
- The operation’s current availability and price have been checked on its API page.