Decision guides
Each guide starts with what the caller actually has, then selects a documented goal and public operation. The OpenAPI contract remains authoritative for exact schemas and errors. If a branch below says “not documented”, stop rather than inventing an endpoint.
I have a VIN
Goal: retrieve vehicle data
- Call
GET /vin/{vin}/vehicle. Optional query parameters arecountry(two-letter code, defaultde),provider(1,2or3, default1) and the flagsincludeEquipments,includeColorsandincludeTechnical(each defaulttrue). - If the response is
409redirect_required, create a browser session withPOST /vin/redirect-sessions, keep the API key in the customer system and hand only the returnedredirectUrlto the browser; on completion or cancellation the user returns to the validatedreturnUrl, whose query string the API extends bystatus,tapiIdandstate. Otherwise consume the direct200result or the404no-result response (not_foundorvehicle_not_found). - Read
tapiId, the numericproviderused for the result,incompleteand the documented vehicle data from the success response. - Persist the VIN and
tapiIdseparately;tapiIdrepresents the matched technical vehicle type, not the individual physical car. - Continue with
GET /vehicles/{tapiId}for technical vehicle data orPOST /vehicles/{tapiId}/listingfor listing text when needed.
Treat the numeric provider value as an opaque selection agreed by contract. Do not infer suppliers or data provenance from it. Which values an operation accepts is fixed per operation: GET /vin/{vin}/vehicle and GET /vin/{vin}/parts accept 1, 2 or 3, GET /vin/{vin}/economic-evaluation accepts only 1, and a 202 VIN-parts job always reports provider 1.
Goal: retrieve vehicle-related parts
- Call
GET /vin/{vin}/partswith a full VIN and the request parameters documented for the customer contract. - Keep the optional numeric selection consistent for the same VIN. A conflicting value returns
409 vin_provider_mismatch. - On
200, mapparts[], including each requiredtapiGenArtandvdifield, plusmatchLevel(vehicle_specific_best_available,vehicle_type_candidatesorvehicle_specific_unverified),provider, the nullabletapiIdandmissingCategoriesaccording to the schema; a non-emptymissingCategoriesmeans the parts list is incomplete. Preservevdi=[]as the complete value for a position without a valid mapping. - On
202, persist the job and poll the required fullstatusUrlwithGET /vin/parts/jobs/{jobId}afterRetry-After. CompareLocationwith that documented URL and quarantine a mismatch rather than resolving its current root-relative example against the API origin. - Treat
matchLevelas part of the result, not optional display text.
Do not retry the order merely because it returned 202. Do not discard positions. A successful VIN-parts position always includes exactly one tapiGenArt and one vdi array; an empty vdi array does not keep the job pending.
Goal: check an OE-number cart against the VIN
Use POST /vin/cart-check with the required vin (17 characters), mode and one to 30 oeNumbers, not a parts lookup or SEO response. mode=type checks at vehicle-type level and mode=vehicle checks the specific vehicle; only a vehicle check that cannot finish immediately returns 202, which is polled with GET /vin/cart-check/jobs/{jobId}. Read every results[].fits together with complete: fits=true means the number itself or a documented replacement number appears in the vehicle's parts list, and fits=false is a reliable exclusion only when complete=true.
Goal: match vehicle recalls
Call GET /recalls/vehicles/{vin}. The match is series-level: every entry in measures[] names its register (kba, eu_safety_gate or nhtsa) and carries a match with confidence (strong, medium or weak) and matchedOn, an array whose entries are manufacturer, model or constructionPeriod; registers[] states each register's dataStand, which can be null. Keep the always-present notice (de, en, fr) with the result. A VIN that cannot be resolved returns 404 vin_not_resolvable. Do not turn a match into a statement that a particular physical vehicle is affected beyond what the response schema says.
Goal: calculate an economic evaluation
Call GET /vin/{vin}/economic-evaluation only with a known full VIN. The optional query parameters are country, provider (only 1), condition (used or new, default used), vehicleType (car or motorcycle, default car), maxPricedParts (1 to 100, default 100), recoveryRate (0.05 to 1, default 0.5) and costPerPart (0 to 1000, default 0). On 202, poll the required statusUrl for GET /vin/economic-evaluation/jobs/{jobId}. Keep the returned assumptions (maxPricedParts, recoveryRate, costPerPartEur, priceBasis), the coverage counts, matchLevel and any warnings with the evaluation; the price evaluation behind it is indicative.
Goal: calculate cluster-based VIN Economic Intelligence
Call POST /vin/economic-intelligence (createVinEconomicIntelligence) with the required body fields vin (17 characters), marketCountry, currency and condition (new, remanufactured, used or unknown) and a required Idempotency-Key (16 to 100 characters from A-Z, a-z, 0-9, _ and -). This follows the documented VIN-to-vehicle-parts-to-part-cluster Intelligence flow and is distinct from GET /vin/{vin}/economic-evaluation: its final result exposes evaluationId, pipeline coverage, a risk-adjusted portfolio, cluster candidates, confidence, provenance and warnings. The route belongs to a closed, allowlisted commercial pilot and currently returns 503 until VIN Economic Intelligence, approved positive pricing and atomic billing are enabled together. Billing is checked first: while Intelligence billing is not enabled with valid prices, the route returns market_intelligence_billing_unavailable, which is the current response. Only once billing is available does an authenticated pilot request return market_intelligence_vin_not_enabled while VIN Intelligence is still disabled. After enablement, a 202 is polled only through GET /vin/economic-intelligence/jobs/{jobId} (getVinEconomicIntelligenceJob); that status operation can continue to return 202 while work is pending. Do not automate acquisition from a result whose confidence, missing evidence or candidate exclusions violate the caller's review policy.
I have an OE number
Goal: validate and normalize the number
Call GET /parts/oe/normalize with the required query parameter oeNumber (1 to 128 characters) and, when known, the optional manufacturer (1 to 100 characters). status is one of matched, unresolved, ambiguous or invalid. Use the returned status before reading normalizedOeNumber; the latter is null for every status other than matched.
Do not implement a local uppercase/whitespace rule and call it equivalent. Other OE operations state their own input treatment and may not use the same normalization behaviour.
Goal: retrieve the public base part
Call GET /parts/oe/{oeNumber}. A 200 response is a closed public object containing exactly:
oeNumbernormalizedOeNumbertapiGenArtvdipartfitmentreplacementChainreferencesreferenceNumbers
No confirmed base hit returns 404 (not_found or oe_part_not_found); an ambiguous input can return 422 ambiguous_oe_number. The required part object proves that a base record exists, but its public enrichments manufacturer, name and listPrice can each be null. Preserve part.name as the returned string or null. The response always includes tapiGenArt as a valid code or null and vdi as an array of valid full codes or []; they are independent best-effort enrichments, and missing classification data does not invalidate the confirmed base part. tapiGenArt, vdi, fitment, a comparison number or a replacement edge cannot independently create a successful base-part hit.
Interpret the four relationship views separately. fitment contains the available type-level vehicle assignments and can be empty. references[] groups confirmed reference and comparison numbers by manufacturer, while referenceNumbers.oe_oem_reference_numbers[] is the consolidated confirmed list including the confirmed OE number. replacementChain carries confirmed directed from/to edges. Do not derive any of these views from another.
Goal: retrieve aftermarket references
Call GET /parts/oe/{oeNumber}/aftermarket-references. The operation compares the submitted OE part number; its contract documents no OE normalization step, and its response schema defines no normalizedOeNumber. If no references are available, aftermarketReferences is an empty list. With the optional limit (1 to 500) and offset (only together with limit), count stays the total and page.returned is the page length; each page is a separate call and is charged like a call. A reference does not guarantee technical compatibility, interchangeability, fit or suitability. If the business workflow needs a confirmed base part, run the base OE lookup separately rather than treating a reference result as equivalent.
Goal: evaluate price
Use GET /parts/oe/{oeNumber}/price with the optional condition (new or used, default used), currency (ISO 4217 code) and vehicleType (car or motorcycle, default car). Branch on result first: priced means usable listings were found and the ranges are evidenced; no_listings_found means every value is 0 and must not be read as a market price. If nothing could be measured at all, the operation answers with an error status instead of a result. The response carries separate new and used ranges (min, max, average) and a priceRecommendation with confidence (HIGH, MEDIUM or LOW); keep currency with every figure. The output is an indicative evaluation, not a binding purchase, sale or price guarantee.
Goal: resolve a part cluster and inspect market intelligence
Call GET /parts/intelligence (resolvePartIntelligence) with at least one of oeNumber or aftermarketNumber, the matching oeManufacturer or aftermarketManufacturer namespace when known, and optional kType, natCode or genArt context. A required Idempotency-Key represents one business request even though this is a GET. The current closed, allowlisted commercial pilot does not deliver real Intelligence results until approved positive pricing and atomic billing are active. Approved Observed Supply data and the authorised dealer panel are not yet available in production, and adaptive refresh is disabled; without an eligible materialized snapshot, the route returns 422 refresh_unavailable (when asOf is set, 422 insufficient_evidence) instead of starting a refresh. kType, NatCode and GenArt can disambiguate but cannot independently establish cluster identity. On 200, retain the returned clusterId, clusterVersion, scope, timestamps, separated observedSupply, verifiedSales and internalUsage evidence, prices, standtime, trend, scores, confidence and provenance. After refresh enablement, a 202 is polled only through GET /parts/intelligence/jobs/{jobId} (getPartIntelligenceJob). Never map observed listings into verified sales or consume an Opportunity Score without its interval, factors, limitations and confidence.
Goal: optimise an eBay or marketplace article listing
Call GET /parts/oe/{oeNumber}/seo with the target marketplaceId, the content language and vehicleType. This one operation answers every "optimise my eBay article", "which category and item specifics does this part belong in" and "write the listing title for this OE number" goal. There is no separate eBay endpoint: the documented marketplaceId values are the eBay marketplaces EBAY_AT, EBAY_AU, EBAY_BE, EBAY_CA, EBAY_CH, EBAY_DE, EBAY_ES, EBAY_FR, EBAY_GB, EBAY_HK, EBAY_IE, EBAY_IT, EBAY_NL, EBAY_PL, EBAY_SG and EBAY_US, with EBAY_DE as the default. language accepts de, en, fr, es, it, nl, pl and zh.
A successful response carries the listing-ready output for that marketplace and language:
content.ebayTitle— the article title for the eBay listing headline, inside the documented 80-character limit.categoryId— the category identifier for the requested marketplace; for anEBAY_*marketplace, the eBay category. Required-present andnullwhen none is returned.itemSpecifics[]— the article's item specifics as{name, value[]}entries, one name able to carry several values.keywords[]— search terms for the listing and shop search.product—manufacturer,nameandnameAdditionfor product naming.content.title,content.h1,content.metaTitle(each at most 80 characters),content.metaDescription(at most 160 characters),content.slug(only lowercase lettersatozand digits, joined by single hyphens) andcontent.bulletPoints(at most 5 entries of at most 160 characters) — the shop-side SEO text.
A no-match is the documented, uncharged HTTP 404 PartSeoNoMatchResponse, not a success variant. While live enrichment is paused, the operation answers 429 seo_live_requests_paused; that response always carries the liveAvailability object with availableAt and retryAfterSeconds, and the X-Tapinoma-Live-Available-At header states the earliest retry. The published 200 schema additionally permits an optional liveAvailability object. Send no Idempotency-Key. Review the returned copy before publication; it is publication content and never a fitment claim.
Goal: generate publication images
Call POST /vision/part/generate with the required oeNumber; a successful call answers 202, and the job is polled with GET /vision/part/generation-jobs/{jobId} until status is succeeded or failed. This is image generation from an OE number, not evidence that a photographed stock item has the same appearance or condition. sourceMode defaults to references_required; keep result.basis (reference_images or knowledge), synthetic, disclosure and limitations with the generated views.
Goal: check part recalls
Call POST /recalls/parts with one to 100 positions[]. A position carries an oeNumber, an optional caller reference (up to 200 characters, returned unchanged) and optional vehicle context (make, model, productionFrom, productionTo), which the operation recommends including when available. The response preserves position order; keep each position's status (matched, no_measures or unresolved), unresolvedReason and measures[] together with registers[].dataStand. If no position can be assigned to a manufacturer, the call answers 404 recall_positions_unresolvable. Do not substitute this for an OE base lookup or vehicle compatibility check.
Goal: retrieve documented vehicle-type assignments
Call GET /parts/oe/{oeNumber} and read fitment[] for the documented reverse OE-to-vehicle-type assignments. The list contains the available assignments and can be empty.
Each result carries an opaque vehicleTypeKey plus its recorded criteria[]; it does not include make/model data and is not a VIN-specific installation guarantee. Criteria are unstructured display text without a documented AND/OR relationship, polarity or executable vocabulary. Preserve them with their key and do not turn them into local compatibility rules. An empty list means no assignment evidence could be returned under the rules above, not that the part fits no vehicle.
API 1.71.0 has no public operation that resolves vehicleTypeKey to make, model or tapiId, and the contract makes no worldwide completeness promise. If the question concerns one known vehicle, call POST /vin/cart-check with its vin, a mode and the OE number in oeNumbers[] instead. Never substitute aftermarket references, VDI codes, references, referenceNumbers or replacement-chain edges for either evidence scope.
I have a clusterId
Goal: read the current scoped snapshot
Call GET /parts/clusters/{clusterId}/intelligence (getPartClusterIntelligence) with a required Idempotency-Key and explicit marketCountry, currency, condition and windowDays (30, 60, 90, 180 or 365); omitted values fall back to their documented defaults. Supply clusterVersion when the workflow must pin the cluster composition. While adaptive refresh is disabled, no 202 occurs. While market-intelligence billing is not enabled, the route answers 503 market_intelligence_billing_unavailable; once billing is enabled, a request without an eligible snapshot returns 422 (refresh_unavailable, or insufficient_evidence with a historical asOf), and a stale snapshot is returned with a stale_snapshot warning unless freshnessPolicy=require_fresh, which returns 422 refresh_unavailable. After refresh enablement, poll a 202 through GET /parts/intelligence/jobs/{jobId} (getPartIntelligenceJob); keep the returned scope and cluster version attached to the final result.
Goal: compare historical snapshots
Call GET /parts/clusters/{clusterId}/history (getPartClusterIntelligenceHistory) with the required from and to dates and Idempotency-Key, explicit marketCountry, currency and condition, and the optional granularity (day, week or month). Preserve each point's asOf, clusterVersion, evidence, scores, confidence and provenance.snapshotId. This operation reads materialized history and has no 202 branch: it does not create missing snapshots or start a backfill job.
I have a vehicle registration document
Goal: extract normalized international document fields
For new integrations, call POST /scanner/document/registration/international with the exact fileUrl request property and omit quality or set it to standard. Personal-document extraction offers no other processing level. The closed RegistrationDocumentV2Response carries format (always tapinoma.vehicle-registration.v2), document, registration, holder, inspection, vehicle, fields[] (every readable header, harmonized and national field in document order) and warnings[]. To map the codes printed by the 32 country profiles to canonical response fields, use the vehicle-registration field mapping: the Markdown version has German explanatory text, while its tables, like the CSV and JSON downloads, use the English canonical field names.
Use POST /scanner/document/registration when an integration deliberately needs the RegistrationDocumentEnvelopeResponse: status (always ok) plus data, whose field descriptions refer to the German registration certificate part I (for example registrationNumber from field A and issuedDate from field I). The deprecated /scanner/registration-document/v2 and /scanner/registration-document paths remain compatibility aliases for existing callers: they do not emit an HTTP redirect and each returns the documented response of its canonical counterpart.
Goal: create a vehicle file in one workflow
Call POST /vehicles/intake with the required fileUrl of the registration document, the optional country for the vehicle match (default de) and optionally one to five walk-around photoUrls of the same vehicle. This operation combines registration-document analysis and vehicle matching; when photos are supplied, the response also includes a condition report and that component is billed. Keeper data is returned only with includeOwner=true; otherwise the keeper block registrationDocument.owner remains null. The closed response carries format, registrationDocument, provider, the nullable vin, tapiId, vehicle and conditionReport, plus components and complete; use tapiId for later calls only when it is non-null. Use it when the target workflow is vehicle intake, not when the caller only needs OCR fields.
Goal: extract a calculation with its documented equipment
Use POST /scanner/document/calculation with the documented fileUrl and optional quality=standard; no enhanced or maximum level is offered for this personal-document operation. It returns the fixed DocumentCalculationResponse: the previous 11 calculation fields plus required equipment[]. The caller cannot submit an output schema or arbitrary field definition. Preserve equipment[].codes[].value as source strings, including leading zeroes, and keep equipmentKind, sourceSection and sourcePage with every row.
The deprecated POST /scanner/document/extract path is a compatibility path for strict clients of the previous contract. It returns the documented 11 top-level fields and omits equipment; it is not an HTTP redirect. Do not reuse one Idempotency-Key across the canonical and compatibility paths: their response projections differ and the API returns HTTP 409 instead of replaying a differently shaped result.
Goal: extract rich vehicle and equipment data from a vehicle document
Use POST /scanner/document/vehicle with fileUrl, optional quality=standard and includeSensitiveData=false unless there is an authorised, purpose-bound need for named parties, contact, bank, payment or contract details. No enhanced or maximum level is offered for this personal-document operation. Choose this operation instead of calculation extraction when the goal is the rich schemaVersion=1.0 vehicle contract: detailed identifiers, powertrain, transmission, energy, colours, equipment[], warnings and the explicit sensitive-data boundary. Preserve equipment codes as strings exactly as returned, including leading zeroes. Keep equipmentKind (standard, variant, special or null) independent from availability; neither field may be derived from the other. The deprecated /scanner/vehicle-document/extract path is a compatibility alias with the same response contract, not a redirect.
Every response is marked Cache-Control: no-store. With sensitive output disabled, an Idempotency-Key may be used for identical retries. With includeSensitiveData=true, do not send that header: the API rejects it and never stores the sensitive response for replay.
Next step after a detected VIN
Validate the response according to its schema, keep the document extraction record separate from the vehicle lookup result, then follow I have a VIN. A detected string is input to a vehicle lookup, not proof that all document fields describe the resulting technical vehicle type.
I have a parts label image
Choose the smallest operation that meets the goal:
| Goal | Operation | Result handling |
|---|---|---|
| Detect whether and where a part number is visible | POST /scanner/label/basic |
Read only hasPartNumber and where (onLabel, onPart); this response contains no recognized text. |
| Extract part/reference-number candidates | POST /scanner/label/extract-partnumbers |
Read partNumbers[], which is empty when no part number was recognised, and confirm each candidate through the appropriate OE operation. |
| Extract all detectable label information | POST /scanner/label/extract-all |
Map the envelope status (always ok) and data; every data field is optional, present values are never null, and data is {} when nothing is detected. Production and sandbox share the same 200 contract; the named examples differ only in their values. primaryPartNumber is a reading, not a confirmed catalogue number. |
Never persist an OCR candidate directly as a confirmed OE number. Keep the source image reference, candidate, confirmation result and operator decision distinct in the customer system.
I have one or more images
Select by outcome, not by a generic “AI image” category.
| Desired outcome | Public operation | Boundary |
|---|---|---|
| Read a visible VIN | POST /scanner/vin/extract |
vin is null when not all 17 characters are reliably readable or more than one number remains plausible; a non-null value matches ^[A-HJ-NPR-Z0-9]{17}$. Continue only after validation. |
| Describe visible damage | POST /vision/damage/describe |
One to five images of the same damage yield a report text with six fixed sections in an unchanging order; truncated marks a text that reached the length cap. Only what is visible is described; repair effort, cost and value are not part of the output. |
| Grade visible part quality | POST /vision/part/quality |
grade is A, B or C; where no reliable statement is possible, gradable is false, grade is null and reason states why. Store it with criteria[], limitations[] and visualOnly, which marks an assessment from images alone. |
| Remove a part-photo background | POST /vision/part/remove/bg |
Generative editing on white only. Use found, nullable imageUrl/media/dimensions, coverage (all values null), sourcePixelsPreserved=false and limitations; compare visible details with the original. The response has no generic asset or provenance object. |
| Remove a vehicle-photo background | POST /vision/vehicle/remove/bg |
Same white-background generative response and review requirement; this is image editing, not vehicle identification. |
| Classify an end-of-life vehicle | POST /vision/vehicle/elv-classification |
classification is kein_altfahrzeug_verdacht, gutachten_empfohlen or altfahrzeug; keep criteria, coverage, economicAssessment (with its basis), limitations and visualOnly with it, and keep all of them separate from a legal disposition. |
| Produce a vehicle condition report | POST /vision/condition-report |
Use for walkaround-condition evidence, not registration-document extraction. |
| Read licence plates | POST /vision/license-plate |
The contract explicitly does not perform keeper lookup, registration validation or authenticity proof. |
| Composite a part into a background | POST /vision/part/composite |
Publication edit with fail-closed identity/redaction checks; not a photo of the actual stock scene. |
| Transfer visible damage to standard views | POST /vision/part/damage-transfer |
Use only for the documented source/target image workflow and preserve synthetic provenance. |
| Redact part identifiers | POST /vision/identifiers/redact |
Redaction is different from reading or validating an identifier. |
Every scanner operation and every Vision operation in the table above carries the usage notice that, by submitting a request, the caller confirms holding all rights, consents and other authorisations required to transmit and automatically process every submitted image, document and URL; personal data may be submitted only on a valid legal basis and limited to what the specific purpose requires. Use only an explicitly approved storage and retention policy.
I have a tapiId
- Use
GET /vehicles/{tapiId}to retrieve documented technical vehicle data for atapiIdfrom a VIN flow previously completed successfully for this client; an unknowntapiIdor one not assigned to the authenticated client returns404vehicle_not_found. - Use
POST /vehicles/{tapiId}/listingto compose marketplace-ready listing text from the documented vehicle data and request inputs. - Do not send
tapiIdwhere an operation requires a VIN. - Do not treat
tapiIdas the identifier of a physical stock vehicle; keep the integrating system's own vehicle-record ID.
I need to check an OE cart against a VIN
Use this sequence:
VIN selected for the Client
-> POST /vin/cart-check with vin, mode (type or vehicle) and 1 to 30 oeNumbers
-> 200: map final response
-> 202 (mode=vehicle only): store jobId + validated statusUrl + nextPollAt
-> GET /vin/cart-check/jobs/{jobId}
-> queued/running: wait according to Retry-After
-> succeeded: map result
-> failed: map error.code (vehicle_not_found = no result, vin_service_unavailable = technical failure); do not treat HTTP 200 as success
The request carries no position IDs: oeNumbers is a plain list of strings, and results[] returns one entry per requested item in the same order and with the same duplicates. Keep the integrating system's own cart-line IDs in request order and reconcile each result by its array index, not by oe alone, because the same number can sit on several independently actionable cart lines.
I need to separate customers or applications
- Keep the Master client API key in the partner backend.
- For a complete partner onboarding action, use
POST /client/partner-workspacesonly when its combined allowlist/key/sponsorship contract matches the workflow. - Otherwise create a Sub-user with
POST /client/users, then configure its rate limits withPUT /client/users/{clientId}/rate-limitsand add keys withPOST /client/users/{clientId}/keysas required. - Store each plaintext API key exactly once when issued; it cannot be retrieved later.
- After an ambiguous network failure, enter operation-specific
reconciliation_requiredstate and never retry a key-issuing operation blindly.POST /client/userspermits partial reconciliation throughGET /client/users, but the lost raw key still cannot be recovered. The public contract provides neither raw-key recovery forPOST /client/users/{clientId}/keysnor lookup byexternalReferenceforPOST /client/partner-workspaces; resubmitting anexternalReferencealready used within the Master client returns409workspace_reference_exists, whose closed error body contains no API key. - Read usage through
GET /client/usageor, as a Master client,GET /client/users/{clientId}/usage.
The integrating system's Tenant remains its own domain object. Map it to a Client; do not add an undocumented tenant field to API calls.
I need a function that is not listed
Search the published OpenAPI contract by goal, tag, operation ID and path. If no matching operation exists under OpenAPI paths, the capability is not part of API 1.71.0. Do not fabricate a call.