# UDS evidence ingestion quickstart

Public documentation: https://api.redlineautohaus.com/docs/uds/

API base: `https://api.redlineautohaus.com/api/v1/uds`
Contract: [OpenAPI](https://api.redlineautohaus.com/docs/uds/openapi.json), [JSON Schemas](https://api.redlineautohaus.com/docs/uds/schemas/)

This service records diagnostic evidence. It does not connect to vehicles or execute diagnostic procedures. The examples below are invented, not captures from a customer's vehicle. Unknown identity, direction and transport stay unknown.

## 1. Obtain a scoped credential

Ask the API operator for a scoped service credential with `uds:read`, `uds:write`, and `uds:sensitive-read` for this byte-read-back example, an approved target environment and permission to create synthetic records there. The server derives the ownership scope from the credential. A public docs URL, an AutoClever login or a client-supplied shop ID does not by itself grant UDS access.

The API uses `Authorization: Bearer <credential>`. Keep the credential in your secret manager or process environment, never in source code, a URL, a screenshot or a ticket. The examples do not create credentials or expand their permissions. `uds:review`, `uds:artifact-read`, `uds:sensitive-read` and `uds:admin` are separately privileged.

For an interactive Bash session, enter the credential without echoing it or putting its value in shell history:

```bash
read -r -s -p 'UDS credential: ' UDS_TOKEN
printf '\n'
export UDS_TOKEN
export UDS_BASE_URL='https://api.redlineautohaus.com/api/v1/uds'
export UDS_RUN_ID='my-synthetic-onboarding-001'
```

Use a different `UDS_RUN_ID` for a genuinely separate onboarding run. Keep the same value when resuming an interrupted run. Do not run with shell tracing (`set -x`), curl verbose/trace output or an HTTP debugger that records Authorization headers. Local process/environment visibility also needs appropriate workstation permissions.

Read capabilities before writing:

```bash
curl --fail-with-body --silent --show-error \
  --header "Authorization: Bearer $UDS_TOKEN" \
  --header 'Accept: application/json' \
  "$UDS_BASE_URL/capabilities"
```

Check the advertised contract, permission information and actual limits. A list of supported permission names is not a grant to your credential: confirm your assigned permissions with the operator. Writes can be administratively disabled while reads/docs remain available. A disabled module, missing retention approval, unavailable database or disabled worker is not fixed by changing retry keys.

## 2. Choose the ready-to-run example

Download these public files into one local directory:

- [Synthetic fixtures](https://api.redlineautohaus.com/docs/uds/examples/fixtures.json)
- [Node.js example](https://api.redlineautohaus.com/docs/uds/examples/quickstart.mjs): Node.js 22+, built-in fetch, bounded retries and read-back assertions.
- [curl example](https://api.redlineautohaus.com/docs/uds/examples/quickstart.sh): Bash, curl and jq; readable request sequence with a stable restart key.

Inspect the script, set the variables above, then run **one** client:

```bash
node quickstart.mjs
# Or, with fixtures.json alongside it:
bash quickstart.sh
```

These scripts write synthetic source/session/message/import records to the credential's scope. They never send diagnostic commands, fetch third-party capture URLs or delete data. They deliberately include invalid hex in a mixed batch; that item's rejection is expected. The Node script also submits an independent invalid single message and verifies its 422 response.

The same `UDS_RUN_ID` produces the same HTTP idempotency keys, source/session keys, occurrence keys and batch key. Retry after uncertainty using the same values and unchanged request bodies. The scripts print resource IDs and safe status, not the credential or a real capture.

## 3. Understand the record sequence

Single-resource mutation responses are resource objects with an `id` and metadata; paginated lists use `data`, `has_more` and a cursor. Consult the generated contract for the route's exact response rather than assuming every response has a `data` wrapper.

| Step | Request | Important fields / result |
| --- | --- | --- |
| Register source | `POST /sources` | `kind`, stable `external_key`, actual `origin`; returns source `id`. |
| Open session | `POST /sessions` | Stable `external_key`; vehicle and ECU identification are optional. |
| Associate evidence source | `POST /sessions/{sessionId}/sources` | `source_id` only; parent session comes from the URL. |
| Record provenance | `POST /source-records` | `source_id`, stable `external_key`, excerpt and/or supported locator; source ordering as a decimal string. |
| Record observation | `POST /messages` | `session_id`, `source_record_id`, `client_namespace`, `external_record_key`, `layer`, `payload`. |
| Verify original evidence | `GET /messages/{messageId}` | Compare exact complete bytes or partial segments; inspect unknown context and origin. |

Every POST requires `Content-Type: application/json` and an `Idempotency-Key`. Do not submit `id`, actor, ownership scope, received timestamp or review state as client-owned fields. Unknown top-level fields are validation errors, not ignored metadata.

A complete, request-only observation looks like this after inserting returned session/source-record IDs:

```json
{
  "session_id": "11111111-1111-4111-8111-111111111111",
  "source_record_id": "22222222-2222-4222-8222-222222222222",
  "client_namespace": "example.uds-docs",
  "external_record_key": "synthetic-request-001",
  "layer": "uds_pdu",
  "direction": "unknown",
  "source_order": "1",
  "extraction_quality": "transcribed",
  "payload": {
    "representation": "complete",
    "payload_hex": "22F186"
  }
}
```

The two UUIDs above are explanatory placeholders, not usable records. The executable scripts create real scoped IDs and substitute them. No response, VIN, make, model year, ECU, transport or decoded service is required to preserve this observation. The layer in an actual submission must come from the source, not from recognizing the sample bytes.

## 4. Preserve partial and unavailable evidence

For a known prefix and unknown trailing bytes:

```json
{
  "representation": "partial",
  "reason": "source_omitted_bytes",
  "segments": [
    { "ordinal": 0, "offset_bytes": 0, "payload_hex": "623F06" }
  ]
}
```

This does not assert a complete response or a total length. An unknown offset is null or omitted, not automatically zero. Never add padding bytes, concatenate across gaps, infer a full length or turn an unreadable screenshot into exact bytes.

For observed but redacted/unavailable bytes:

```json
{
  "representation": "unavailable",
  "reason": "redacted_by_source"
}
```

Include the source record that explains the observation. Unavailable evidence has no payload field/segments. Invalid hex such as `22F18` is rejected rather than silently padded; the original excerpt can still exist as provenance. See [evidence semantics](https://api.redlineautohaus.com/docs/uds/evidence.md).

## 5. Ingest a durable mixed batch

1. `POST /imports` with a stable `client_namespace`, `external_key` and optional `purpose`.
2. `POST /imports/{importId}/message-batches` with a stable `batch_key` and bounded `items` array.
3. Save the returned import and batch IDs. A 202 means durable intake, not that every item is valid or already committed.
4. `POST /imports/{importId}/finalize` with `{}` only after the last intended batch is accepted. This seals intake; pending batches continue processing.
5. `GET /imports/{importId}/message-batches/{batchId}` for status/counts, and `GET /imports/{importId}/message-batches/{batchId}/items` for ordered item outcomes. Do not discard rejected rows when an import has partially succeeded. Outcome pages use a signed opaque `cursor`, `limit`, and optional exact `external_record_key`; follow `next_cursor` when `has_more` is true. Cursors cannot be reused for another scope, batch, or filter.

The provided scripts submit one existing occurrence, one new occurrence and one invalid odd-length payload. With the worker available, the expected terminal batch state is `completed_with_errors`, with one `existing`, one `created` and one `rejected`. A timeout waiting for that result is reported as unfinished, not success.

Batch items normally use an existing `source_record_id`. For atomic provenance-plus-message creation, the batch processor also supports `inline_source_record` instead of `source_record_id`; follow the batch item schema and supply its source ID, stable external key and actual excerpt/locator. Invalid message validation must roll back any newly created inline source record in that item. Do not send both forms together.

Cancelling an import seals further intake and stops remaining work; it does not delete already committed observations. Replaying a cancelled/completed batch does not start a new run. To correct a rejected input, understand the failure, submit a deliberate corrected item under the documented identity rules, and retain any required revision/evidence relationship.

## 6. Use the two replay guarantees correctly

**HTTP idempotency** identifies a request by ownership scope, principal, method/route and `Idempotency-Key`. An identical semantic request returns its original result. A changed body with the same key returns 409. Keep completed keys for at least the advertised 24-hour guarantee; an in-progress operation must not expire while it may still commit.

**Occurrence identity** identifies an observation by ownership scope, `client_namespace`, `session_id` and `external_record_key`, independently of the import. Use it to resume/re-import without duplicating an already stored occurrence. Two different source occurrences with identical bytes remain two messages; payload hashes are not identity keys.

Never replace a key automatically after a network timeout or 409. Inspect the original outcome first. An intended correction is new evidence/revision, not an overwrite of immutable raw bytes.

## 7. Read bounded lists and optional artifacts

Metadata list responses omit raw message bytes. Start with a supported exact filter such as `/messages?session_id={sessionId}&limit=100`, consume `data`, and send the returned `next_cursor` as `cursor` while retaining the same filters. Cursors are bound to scope, resource, filters and a high-watermark. They are not portable across credentials/scopes or changing searches. Import item outcomes instead use their documented ordinal cursor.

Read a single message to verify inline bytes and segments. An artifact-backed message references a verified range; artifact download/range access requires the separate artifact permission. Artifact registration alone does not verify an upload. Follow the returned upload instructions, upload the exact bytes privately, complete verification, and only then reference an available artifact. Never submit a filesystem path or URL expecting the server to fetch it.

No cross-tenant search or anonymous dataset browsing is granted by public documentation. Unsupported filters return a client error rather than enabling arbitrary JSON/SQL search.

## Errors and completion

[Error handling](https://api.redlineautohaus.com/docs/uds/errors.md) explains 400/401/403/404/409/412/413/422/428/429/503 responses. Preserve the safe `request_id` and `code`; do not attach credentials or full evidence to ordinary logs. Retry transient failures only with bounded backoff, unchanged request identities and `Retry-After` where supplied.

A client run is complete only after exact read-back checks and terminal item outcomes have passed. Keep unresolved context explicitly unknown. This onboarding example does not prove production throughput, restore correctness, retention approval, vehicle applicability or the safety of executing any diagnostic request.

When finished with an interactive session, clear the temporary credential variable:

```bash
unset UDS_TOKEN
```

## Shared account API keys

Create a key in [RedlineAutohaus account settings](https://redlineautohaus.com/account/settings/api). Use it as `UDS_TOKEN` in these examples. Every active key includes evidence submission and creates its own account workspace automatically. See the [API access guide](../api-access/guide.md) for optional sensitive-read permissions, quotas, published knowledge and administrative capabilities.
