← Back to product·Simulmedia VAMOS Docs · v1 draft·API reference / Files
API reference

Files

File interface: S3 and SFTP drops

The zero-integration way in: publish your catalog, rate cards, and avails as file drops, and return delivery posts through the same channel. A drop is a set of files landed under your prefix, over S3 or SFTP, registered by a manifest. Accepted formats per type: csv, parquet, xlsx, json.

s3://vamos-ingest/{seller_id}/{drop_id}/
  catalog.csv
  rates.csv
  avails.csv
  manifest.json

Ingestion is asynchronous; a clean drop publishes in minutes. The result lands back in the drop as success.json, or as errors.json naming every rejected row and its code:

{
  "drop_id": "drop_2026-W40-001",
  "status": "partial",
  "rows_written": 840,
  "errors": [
    {
      "file": "avails.csv",
      "row": 147,
      "error_code": "UNKNOWN_PACKAGE_ID",
      "value": "acme-fringe-old"
    }
  ]
}
Catalog and rate-card drops pass the same integrity checks as API pushes. A rejected update also emits a catalog.integrity_failed event with the conflict report attached.

Register a file drop

POST/v1/me/files/manifestcatalog:write

Register the manifest for a landed drop; ingestion starts on registration.

Request body

FieldTypeDescription
schema_version requiredstringManifest schema version. Current version: 1.0.
drop_id requiredstringThe drop the files landed under.
effectiveobjectCoverage window the drop asserts: {start, end}, ISO dates.
files[] requiredarray of objectOne entry per landed file.
files[].filename requiredstringFile name inside the drop.
files[].type requiredenumWhat the file carries: catalog, rate_cards, avails, or delivery_posts.
files[].formatenumcsv, parquet, xlsx, or json. Default: inferred from the file extension.
files[].sha256 requiredstringChecksum of the landed file; the file's identity for idempotency.

A manifest for a full catalog drop:

{
  "schema_version": "1.0",
  "drop_id": "drop_2026-W41-001",
  "effective": {
    "start": "2026-10-05",
    "end": "2026-12-27"
  },
  "files": [
    {
      "filename": "catalog.csv",
      "type": "catalog",
      "format": "csv",
      "sha256": "9f86d081..."
    },
    {
      "filename": "rates.csv",
      "type": "rate_cards",
      "format": "csv",
      "sha256": "b94f6f12..."
    },
    {
      "filename": "avails.csv",
      "type": "avails",
      "format": "csv",
      "sha256": "3a7bd29c..."
    }
  ]
}
Idempotency is by file hash: re-registering a manifest whose sha256 values all match an already-processed drop replays the original result instead of reprocessing.

Response

Registration is accepted and processing starts; results land in the drop as success.json or errors.json.

202 Response
{
  "drop_id": "drop_2026-W41-001",
  "status": "processing",
  "results_uri": "s3://vamos-ingest/acme/drop_2026-W41-001/"
}

Errors

StatusCodeWhen
404NOT_FOUNDdrop_id does not match a landed drop under your prefix.
422INVALID_INPUTA sha256 does not match the landed file, or a manifest entry names a file missing from the drop.

Upload a file (buyer)

POST/v1/uploadsorders:write

Where a buyer's file comes from. Register the file, PUT the bytes, then reference the upload_id: on a document order's document.upload_id, or as an attachment alongside a structured order. The seller-side drop manifest above is a different thing entirely: that is a seller publishing its catalog, this is a buyer handing over one document.

Request body

FieldTypeDescription
filename requiredstringThe name as you hold it. Recorded, never interpreted: the format check reads the bytes, not the extension.
format requiredenumpdf, xlsx, or csv. A declaration, verified against the bytes when they arrive: pdf and xlsx by signature, csv structurally (see the callout under Send the bytes).
size_bytes requiredintegerThe size you are about to send. Over the platform maximum, the mint is refused before you transfer anything.

Response

201 Response
{
  "upload_id": "upl_4b2c91d0e7a3",
  "buyer_entity": "yourco",
  "filename": "q4-buysheet.pdf",
  "format": "pdf",
  "size_bytes": 48211,
  "status": "pending",
  "sha256": null,
  "expires_at": "2026-10-06T00:00:00Z",
  "created_at": "2026-10-05T00:00:00Z",
  "upload_url": "/v1/uploads/upl_4b2c91d0e7a3/content"
}

Errors

StatusCodeWhen
422INVALID_INPUTAn unsupported format, a missing filename, or a size_bytes over the platform maximum. details.field names which one. Nothing is minted.

Send the bytes

PUT/v1/uploads/{id}/contentorders:write

PUT the raw bytes to the upload_url from the mint. The platform hashes what arrives and checks it against the format you declared.

200 Response
{
  "upload_id": "upl_4b2c91d0e7a3",
  "status": "uploaded",
  "sha256": "9f2c...",
  "size_bytes": 48211
}

What the csv check accepts. CSV has no signature, so the check is structural: every non-empty row carries the same number of fields as the header, quoted commas are respected, and a leading {, [, or < is refused because JSON and XML are what gets sent by mistake. Single-column files are valid and accepted. Ragged rows, binary, and anything that is not valid UTF-8 are refused.

Exactly one PUT. Once a hash exists the record is read-only and a second PUT returns 409 CONFLICT with the stored hash untouched. That is deliberate: an attachment whose bytes could be replaced after its hash was checked is not evidence of anything. If you sent the wrong file, mint a new upload; if it is already on an order, revise the order.

Errors

StatusCodeWhen
404NOT_FOUNDNo upload with that ID under your credentials. An upload belonging to a different acting buyer reads as 404 rather than 403, so an ID never confirms whose it is.
409CONFLICTThe upload is not pending: it already has bytes, was rejected, or expired. details.status says which.
422INVALID_INPUTThe bytes contradict the declared format. The record becomes rejected and can never be attached; details.detected_format says what arrived, or null when nothing recognisable did.

Read an upload

GET/v1/uploads/{id}orders:write

The current state of one upload: its status, and its sha256 once bytes have arrived. Buyer-only. A seller never reads an upload directly; it reads a document or attachment through the order it belongs to, so an upload you never reference is invisible to every seller.

FieldTypeDescription
statusenumpending (awaiting bytes), uploaded (bytes and hash recorded, ready to reference), rejected (contradicted its declared format), expired. No status ever returns to pending.
sha256stringOf the bytes as received. null until they arrive. Check it against your own copy.
expires_atISO datetimeWhen an unreferenced upload lapses. See the callout below.

Expiry bounds an upload no order references, and an upload on an order is pinned. An upload that never received bytes, and one that received them and was never used, both lapse to expired past expires_at and can no longer be referenced. The moment an order references it, the expiry stops applying: an order that reaches placed carrying a buysheet whose bytes later vanished would be worse than one that never carried it, so an attached file stays resolvable for the life of its order, terminal states included. There is no delete operation; to change what an order carries, revise the order.

EDI (agency and traffic systems)

EDI is the TV industry's electronic paper trail, and it runs in both directions: station traffic and billing systems generate it (invoices, as-run detail), and agency systems of record (Prisma, Strata) consume it. VAMOS plugs into both ends of that flow.

Out, for buyers. If your billing lives in an agency system of record, VAMOS delivers confirmed orders over the established interchange (structured XML via the industry's email transport), so order and billing records land in the same place your current orders land. Include your estimate_id in buyer_metadata and it is echoed on the delivery, so your billing system can match the record.

In, for sellers. Your traffic system already produces EDI invoices and as-run log times. Point those feeds at VAMOS like any other file drop and they attach to orders as reconciliation inputs: air dates, times, and copy against what was booked, in the paper your plant already prints.

Both directions ride your agreement; neither changes the API contract. Request EDI delivery on the buyer side, or register your traffic system's feeds through the file interface above.