Bulk data requests

A bulk data request — a set — is one data request definition over many passports: the same ontology, the same level, the same requested properties, asked of a list of passports or of every child of one passport at a level. Each passport gets an ordinary data request of its own, the set's child, which is answered, reviewed and applied exactly as a single request is. The set is the bulk view on top: one page of children, one Excel workbook out and back, one mass fulfilment from the supplier's own passports, one submit all and one accept and apply all.

Kestrel Energy selects the cells of a pack and asks Nordvolt Cells for each cell's capacity and supplier part number. Nordvolt downloads the workbook, fills two columns and uploads it — or matches the rows to its own cell passports and lets Synex draft the answers — then submits all; Kestrel accepts and applies all. Both teams read the same set, each from its own side.

A child carries set: {id, position, identifier_value}. GET …/children lists one set's children in row order; they also appear in GET /v1/data_requests beside single requests, told apart by a non-null set. Each is read, answered, reviewed or applied one at a time through /v1/data_requests/:id like any other request. The set travels over a supplier link, as a single request does.

Sides and abilities

The abilities are the data requests' own. Opening a set and applying it also need passports:write, because a request is a passport write taken on credit.

OperationAbilitySide
GET /v1/data_request_setsdata_requests:readEither — direction picks
POST /v1/data_request_setsdata_requests:write and passports:write covering the passports' ontologyCustomer
GET /v1/data_request_sets/:id, …/children, …/jobs/:jobdata_requests:readEither
GET …/export, GET …/uploads/:uploaddata_requests:readEither
POST …/uploadsdata_requests:writeSupplier
GET …/fulfil/sourcesdata_requests:writeSupplier
POST …/fulfil/matchdata_requests:write, plus passports:read on the chosen ontologySupplier
POST …/fulfil/filldata_requests:write, plus passports:read on each sourceSupplier
POST …/submit_alldata_requests:writeSupplier
POST …/apply_alldata_requests:write and passports:write covering the passports' ontologyCustomer
POST …/canceldata_requests:writeCustomer

Every POST accepts an optional Idempotency-Key header, the upload included.

The lifecycle

A set is creating while a queued job makes its children, open once they exist and at least one is not terminal, and closed once every child is — closed_reason: "settled" when they all settled, "cancelled" when the customer cancelled the set. The status is derived from the children and never set directly: a child applied through its own POST /v1/data_requests/:id/apply moves the set's counts as a bulk apply would, and the last child to settle closes the set.

counts tallies the children by status and is where a poller reads how far an open set has got. While the set is creating, progress mirrors the create job's counters and create_job names the job.

Jobs

Every bulk action — making the children, importing an upload, drafting from your passports, submitting all, applying all — runs as a queued job. The call answers with a data_request_set_job row (202; a create answers 201 with the set and names the job in create_job). Poll GET …/jobs/:job until status is done or failed: progress.done and progress.total move while it runs, progress.failed names what it could not process and why, and the kind's own lists say what became of the rest. Nothing is rolled back across children — one refusal never undoes another child's success.

kindQueued byLists in progressresult once terminal
createOpening a setfailed: {id, error}, id the passport's pass_ id{created, failed}
importAn uploadnone — done and total count sheet rows; refusals are on the upload's errors{rows, valid, invalid, submitted}
fillfulfil/fillfilled: {id, source, filled, of, caveats}; failed: {id, source, error}{requests, filled, of, failed}
submitsubmit_allsubmitted, applied, blocked: {id, label, reason}; failed: {id, label, error}{submitted, applied, blocked, refused}
applyapply_allapplied, returned: {id, label}; failed: {id, label, error}{applied, returned, failed}

In those rows id is the child's dreq_ id (except on create), label its passport label, error the code the single operation was refused with, and reason the apply_blocked_reason of a child whose automatic apply was held back. A fill's filled and of count the fields it drafted over the fields still open, and caveats carries the converter's {property_id, kind, message} notes on what it copied. A failed job adds error to result; a create job cut short by a cancel ends done with stopped: "set_closed".

The data request set model

  • Name
    object
    Type
    string
    Description

    Always data_request_set.

  • Name
    id
    Type
    string
    Description

    Unique identifier, e.g. dset_7b3e9c1a5d2f8460. The same on both sides.

  • Name
    status
    Type
    string
    Description

    creating, open, or closed — see the lifecycle.

  • Name
    closed_reason
    Type
    string
    Description

    settled or cancelled once the set is closed; null until then.

  • Name
    passport_count
    Type
    integer
    Description

    How many passports the selection resolved to — the number of children once they are made.

  • Name
    depth
    Type
    object
    Description

    The level every passport of the set sits at: its key and name (nullable).

  • Name
    ontology
    Type
    object
    Description

    The passports' ontology: its id, the version pinned at create, and the default_language a bare answer to a multilingual property is read in.

  • Name
    properties
    Type
    array
    Description

    The requested property ids, in the order the supplier sees them.

  • Name
    definitions
    Type
    array
    Description

    The requested properties' definitions as pinned at create — id, name, type, value (the type's rules) and data_element_id where set. Every answer is validated against these.

  • Name
    identifier
    Type
    object
    Description

    What the supplier matches rows on: {"kind": "friendly_id"} for the customer's friendly ids, or {"kind": "property", "property_id", "name", "type"} for a property of the level. null when the customer marked none.

  • Name
    selection
    Type
    object
    Description

    The selection as the customer gave it — mode: "list" with the ids (trimmed, each once), or mode: "children" with the parent's pass_ id and the level.

  • Name
    message
    Type
    string
    Description

    The customer's note to the supplier, shown on every child. Nullable.

  • Name
    auto_apply
    Type
    boolean
    Description

    Whether each child's submission is applied the moment it arrives.

  • Name
    due_at
    Type
    string
    Description

    The advisory deadline, ISO 8601. Nullable.

  • Name
    supplier
    Type
    object
    Description

    The supplier link the set travels over, as it stands now: id (sup_…), name and status (pending, linked or revoked).

  • Name
    customer_team
    Type
    object
    Description

    The customer team: id and name.

  • Name
    supplier_team
    Type
    object
    Description

    The supplier team: id and name. null while the link's invitation is unaccepted.

  • Name
    requested_by
    Type
    object
    Description

    The user who opened the set: id and name. Nullable.

  • Name
    cancelled_by
    Type
    object
    Description

    The user who cancelled the set. Nullable.

  • Name
    counts
    Type
    object
    Description

    The children by status — total, open, submitted, changes_requested, applied, cancelled. Computed per read. total is the number of children made so far, below passport_count while creating.

  • Name
    create_job
    Type
    string
    Description

    The dsj_ id of the job that made (or is making) the children.

  • Name
    progress
    Type
    object
    Description

    The create job's done, total and failed while the set is creating; null otherwise.

  • Name
    created_at
    Type
    string
    Description

    ISO 8601 timestamp of creation.

  • Name
    closed_at
    Type
    string
    Description

    ISO 8601 timestamp the set closed. Nullable.

  • Name
    updated_at
    Type
    string
    Description

    ISO 8601 timestamp of the last change.

  • Name
    viewer_role
    Type
    string
    Description

    customer or supplier — which side of the set your team is on. Present on a single set (a read, a create, a cancel), not on list rows. Both sides see the same fields.

The data request set job model

  • Name
    object
    Type
    string
    Description

    Always data_request_set_job.

  • Name
    id
    Type
    string
    Description

    Unique identifier, e.g. dsj_9c2e4a7b1d8f3056.

  • Name
    set
    Type
    string
    Description

    The dset_ id of the set the job runs on.

  • Name
    kind
    Type
    string
    Description

    create, import, fill, submit, or apply — see jobs.

  • Name
    status
    Type
    string
    Description

    queued, running, done, or failed. The last two are terminal.

  • Name
    progress
    Type
    object
    Description

    done, total and failed always, plus the lists the kind tracks.

  • Name
    result
    Type
    object
    Description

    The kind's summary once the job is terminal, with error added on a failure. null until then.

  • Name
    started_by
    Type
    object
    Description

    The user who started the job: id and name. Nullable.

  • Name
    created_at
    Type
    string
    Description

    ISO 8601 timestamp the job was queued.

  • Name
    updated_at
    Type
    string
    Description

    ISO 8601 timestamp of the last progress write.

The data request set upload model

  • Name
    object
    Type
    string
    Description

    Always data_request_set_upload.

  • Name
    id
    Type
    string
    Description

    Unique identifier, e.g. dsu_4e1b7d9a2c6f8035.

  • Name
    set
    Type
    string
    Description

    The dset_ id of the set.

  • Name
    job
    Type
    string
    Description

    The dsj_ id of the import job reading the workbook.

  • Name
    status
    Type
    string
    Description

    processing, done, or failed, following the job.

  • Name
    submit_on_upload
    Type
    boolean
    Description

    Whether every row that imported clean was submitted straight away.

  • Name
    file
    Type
    string
    Description

    The file_ id of the workbook as kept, in the supplier's Drive.

  • Name
    errors_file
    Type
    string
    Description

    The file_ id of the errors workbook — the uploaded sheet with an Error column — in the supplier's Drive. It is written the first time someone downloads the errors in the Synex app, not by the API, so an API-only integration sees null here and reads errors instead.

  • Name
    summary
    Type
    object
    Description

    Once done: rows (data rows read), valid (rows drafted), invalid (rows refused), submitted (rows submitted under submit_on_upload), and sheet (the 0-based index of the worksheet read). null while processing.

  • Name
    errors
    Type
    array
    Description

    Every refusal, at most 500: row (the 1-based sheet row, as Excel numbers it), column (the heading concerned — Passport ID for a row that could not be matched; null when the sheet has no Passport ID column), property_id (nullable), code, message, and passport (the row's pass_ id once matched, otherwise null). See the codes.

  • Name
    uploaded_by
    Type
    object
    Description

    The user who uploaded the workbook: id and name. Nullable.

  • Name
    created_at
    Type
    string
    Description

    ISO 8601 timestamp of the upload.

  • Name
    updated_at
    Type
    string
    Description

    ISO 8601 timestamp of the last change.


GET/v1/data_request_sets

List bulk data requests

Lists your team's sets, newest first and without viewer_role. direction picks the side: the sets your team opened as the customer, or the ones addressed to it as the supplier. Returns a paginated list. Requires data_requests:read.

Optional attributes

  • Name
    direction
    Type
    string
    Description

    sent (default) — sets your team opened as the customer; received — your inbox as the supplier, where a set still creating is never listed.

  • Name
    status
    Type
    string
    Description

    creating, open, or closed. An unknown value is 400 parameter_invalid rather than an empty page.

  • Name
    supplier
    Type
    string
    Description

    Only sets travelling over this supplier link, by sup_ id.

  • Name
    ontology
    Type
    string
    Description

    Only sets over passports of this ontology, by ontology_ id.

  • Name
    limit
    Type
    integer
    Description

    Page size, 1–100 (default 10).

  • Name
    cursor
    Type
    string
    Description

    A pagination cursor from a previous response's next_cursor.

direction and every filter join the cursor's scope: a cursor minted under one combination is 400 invalid_cursor under another.

Request

GET
/v1/data_request_sets
curl -G https://api.synexcloud.com/v1/data_request_sets \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -d direction=sent \
  -d status=open

Response

{
  "object": "list",
  "data": [
    {
      "object": "data_request_set",
      "id": "dset_7b3e9c1a5d2f8460",
      "status": "open",
      "closed_reason": null,
      "passport_count": 3,
      "depth": { "key": "B", "name": "Cell" },
      "ontology": {
        "id": "ontology_00a1b2c3d4e5f607",
        "version": 3,
        "default_language": "en"
      },
      "properties": ["property_capacity", "property_part_no"],
      "definitions": [
        {
          "id": "property_capacity",
          "name": "Capacity",
          "type": "measurement",
          "value": { "measurement": "mAh" }
        },
        {
          "id": "property_part_no",
          "name": "Supplier part no.",
          "type": "short_text",
          "value": { "required": false }
        }
      ],
      "identifier": { "kind": "friendly_id" },
      "selection": {
        "mode": "children",
        "parent": "pass_3e9a1c5b7d2f4083",
        "level": "B"
      },
      "message": "Cell data for the Q4 audit, please.",
      "auto_apply": false,
      "due_at": "2026-10-15T00:00:00Z",
      "supplier": {
        "id": "sup_7c2e9a4f1b8d3506",
        "name": "Nordvolt Cells",
        "status": "linked"
      },
      "customer_team": { "id": 4821, "name": "Kestrel Energy" },
      "supplier_team": { "id": 5107, "name": "Nordvolt Cells AB" },
      "requested_by": { "id": 9031, "name": "Mara Lindqvist" },
      "cancelled_by": null,
      "counts": {
        "total": 3,
        "open": 2,
        "submitted": 1,
        "changes_requested": 0,
        "applied": 0,
        "cancelled": 0
      },
      "create_job": "dsj_1f4a8c2e6b9d3075",
      "progress": null,
      "created_at": "2026-09-20T09:12:40Z",
      "closed_at": null,
      "updated_at": "2026-09-20T09:40:02Z"
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "url": "/v1/data_request_sets"
}

POST/v1/data_request_sets

Open a bulk data request

Opens one request for named properties over many passports of one level, addressed to a supplier you already have (supplier) or to an address you invite on the spot (recipient). The children are made by a queued create job: the answer is 201 with the set creating, create_job the job to poll and progress its counters. Requires data_requests:write and passports:write covering the passports' ontology — 403 ability_missing (param: "selection") without it, checked before anything is looked up. Accepts an optional Idempotency-Key header.

Required attributes

  • Name
    selection
    Type
    object
    Description

    The passports. {"mode": "list", "ids": [...]} — up to 500 pass_ ids or friendly ids of your team, in the order the rows should take; or {"mode": "children", "parent": "...", "level": "B"} — every passport at level beneath parent (a pass_ id or friendly id). The level must lie below the parent, or it is 400 parameter_invalid on selection.level.

  • Name
    properties
    Type
    array
    Description

    The property ids to ask for — defined at the selection's level in its pinned version, and carrying a fillable value. Duplicates are collapsed. Anything else is 400 parameter_invalid, keyed properties.0, properties.1, ….

  • Name
    supplier
    Type
    string
    Description

    The sup_ id of a pending or linked supplier of yours. Required unless recipient is given; a revoked link is 409 link_revoked.

  • Name
    recipient
    Type
    object
    Description

    {"email", "name"} of an address to invite as a new supplier, opening a pending link with the set. Required unless supplier is given; an address already linked or invited is 409 supplier_already_linked. Name exactly one of the two.

Optional attributes

  • Name
    identifier
    Type
    object
    Description

    What the supplier will match rows on: {"kind": "friendly_id"} for your friendly ids, or {"kind": "property", "property_id": "..."} for a property of the level that carries a value. Omit it for none — the supplier then pairs rows by hand.

  • Name
    message
    Type
    string
    Description

    A note to the supplier, shown on every child. At most 2,000 characters.

  • Name
    auto_apply
    Type
    boolean
    Description

    Apply each child's submission the moment it arrives. Defaults to false.

  • Name
    due_at
    Type
    string
    Description

    An advisory deadline — any date after today.

Resolving the selection

The selection is resolved before anything is written, all or nothing. Every passport must be your team's and share one level of one ontology on one pinned version, anchored by the first passport that is. An id that is not a passport of your team, a friendly id that names more than one passport, or a passport in another ontology, at another level or on another version refuses the whole call with 400 selection_invalid — error.errors keyed by the offending id, one message each — and nothing is opened. More than 500 passports is 400 selection_too_large.

Children are made in the order the passports were given — set.position on each — and the set turns open when the job finishes. data_request_set.created fires on your feed then, and data_request_set.received on the supplier's once a supplier team holds the link.

Request

POST
/v1/data_request_sets
curl https://api.synexcloud.com/v1/data_request_sets \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -H "Idempotency-Key: cells-q4-audit" \
  -H "Content-Type: application/json" \
  -d '{
    "selection": { "mode": "children", "parent": "pass_3e9a1c5b7d2f4083", "level": "B" },
    "identifier": { "kind": "friendly_id" },
    "properties": ["property_capacity", "property_part_no"],
    "supplier": "sup_7c2e9a4f1b8d3506",
    "message": "Cell data for the Q4 audit, please.",
    "due_at": "2026-10-15"
  }'

Response

{
  "object": "data_request_set",
  "id": "dset_7b3e9c1a5d2f8460",
  "status": "creating",
  "closed_reason": null,
  "passport_count": 3,
  "depth": { "key": "B", "name": "Cell" },
  "ontology": {
    "id": "ontology_00a1b2c3d4e5f607",
    "version": 3,
    "default_language": "en"
  },
  "properties": ["property_capacity", "property_part_no"],
  "definitions": [
    {
      "id": "property_capacity",
      "name": "Capacity",
      "type": "measurement",
      "value": { "measurement": "mAh" }
    },
    {
      "id": "property_part_no",
      "name": "Supplier part no.",
      "type": "short_text",
      "value": { "required": false }
    }
  ],
  "identifier": { "kind": "friendly_id" },
  "selection": {
    "mode": "children",
    "parent": "pass_3e9a1c5b7d2f4083",
    "level": "B"
  },
  "message": "Cell data for the Q4 audit, please.",
  "auto_apply": false,
  "due_at": "2026-10-15T00:00:00Z",
  "supplier": {
    "id": "sup_7c2e9a4f1b8d3506",
    "name": "Nordvolt Cells",
    "status": "linked"
  },
  "customer_team": { "id": 4821, "name": "Kestrel Energy" },
  "supplier_team": { "id": 5107, "name": "Nordvolt Cells AB" },
  "requested_by": { "id": 9031, "name": "Mara Lindqvist" },
  "cancelled_by": null,
  "counts": {
    "total": 0,
    "open": 0,
    "submitted": 0,
    "changes_requested": 0,
    "applied": 0,
    "cancelled": 0
  },
  "create_job": "dsj_1f4a8c2e6b9d3075",
  "progress": { "done": 0, "total": 3, "failed": [] },
  "created_at": "2026-09-20T09:12:40Z",
  "closed_at": null,
  "updated_at": "2026-09-20T09:12:40Z",
  "viewer_role": "customer"
}

400

{
  "error": {
    "type": "invalid_request_error",
    "code": "selection_invalid",
    "message": "One or more selected passports cannot be requested together.",
    "param": "selection",
    "errors": {
      "LOT-9": ["\"LOT-9\" is not a passport of this team."],
      "pass_00a1b2c3d4e5f607": ["\"pass_00a1b2c3d4e5f607\" is at level A, not B."]
    },
    "request_id": "req_9f2c1ab34d5e6f70"
  }
}

GET/v1/data_request_sets/:id

Retrieve a bulk data request

Retrieves one set, from either side; viewer_role says which side your team is on. Poll it while creating, or read counts to see how far an open set has got. Requires data_requests:read.

Request

GET
/v1/data_request_sets/dset_7b3e9c1a5d2f8460
curl https://api.synexcloud.com/v1/data_request_sets/dset_7b3e9c1a5d2f8460 \
  -H "Authorization: Bearer $SYNEX_API_KEY"

Response

{
  "object": "data_request_set",
  "id": "dset_7b3e9c1a5d2f8460",
  "status": "open",
  "closed_reason": null,
  "passport_count": 3,
  "depth": { "key": "B", "name": "Cell" },
  "ontology": {
    "id": "ontology_00a1b2c3d4e5f607",
    "version": 3,
    "default_language": "en"
  },
  "properties": ["property_capacity", "property_part_no"],
  "definitions": [
    {
      "id": "property_capacity",
      "name": "Capacity",
      "type": "measurement",
      "value": { "measurement": "mAh" }
    },
    {
      "id": "property_part_no",
      "name": "Supplier part no.",
      "type": "short_text",
      "value": { "required": false }
    }
  ],
  "identifier": {
    "kind": "property",
    "property_id": "property_part_no",
    "name": "Supplier part no.",
    "type": "short_text"
  },
  "selection": { "mode": "list", "ids": ["LOT-1", "LOT-2", "LOT-3"] },
  "message": null,
  "auto_apply": false,
  "due_at": null,
  "supplier": {
    "id": "sup_7c2e9a4f1b8d3506",
    "name": "Nordvolt Cells",
    "status": "linked"
  },
  "customer_team": { "id": 4821, "name": "Kestrel Energy" },
  "supplier_team": { "id": 5107, "name": "Nordvolt Cells AB" },
  "requested_by": { "id": 9031, "name": "Mara Lindqvist" },
  "cancelled_by": null,
  "counts": {
    "total": 3,
    "open": 2,
    "submitted": 1,
    "changes_requested": 0,
    "applied": 0,
    "cancelled": 0
  },
  "create_job": "dsj_1f4a8c2e6b9d3075",
  "progress": null,
  "created_at": "2026-09-20T09:12:40Z",
  "closed_at": null,
  "updated_at": "2026-09-20T09:40:02Z",
  "viewer_role": "supplier"
}

GET/v1/data_request_sets/:id/children

List a set's children

Lists the set's data requests as list rows (no values), in row order — the order the passports were selected in, set.position on each row — not newest first. Either side. To read one child with its values, or to answer, review or apply it alone, use /v1/data_requests/:id: a child is an ordinary request. Returns a paginated list. Requires data_requests:read.

Optional attributes

  • Name
    status
    Type
    string
    Description

    Only children in this status — open, submitted, changes_requested, applied, or cancelled. An unknown value is 400 parameter_invalid.

  • Name
    limit
    Type
    integer
    Description

    Page size, 1–100 (default 10).

  • Name
    cursor
    Type
    string
    Description

    A pagination cursor from a previous response's next_cursor.

The cursor carries the position of the last row handed out, so a walk resumes exactly after it whatever changes on the children in between. It is scoped to the set and the status filter, and opaque like every cursor — a bare position is 400 invalid_cursor. The envelope carries no counts; read them on the set.

Request

GET
/v1/data_request_sets/dset_7b3e9c1a5d2f8460/children
curl -G https://api.synexcloud.com/v1/data_request_sets/dset_7b3e9c1a5d2f8460/children \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -d status=submitted \
  -d limit=100

Response

{
  "object": "list",
  "data": [
    {
      "object": "data_request",
      "id": "dreq_5a1c8e3f9b2d7046",
      "status": "submitted",
      "closed_reason": null,
      "apply_blocked_reason": null,
      "auto_apply": false,
      "message": null,
      "due_at": null,
      "revision": 1,
      "passport": {
        "snowflake": "pass_6b0d3f8a2c5e1794",
        "label": "LOT-1",
        "ontology_id": "ontology_00a1b2c3d4e5f607",
        "ontology_version": 3,
        "default_language": "en"
      },
      "set": {
        "id": "dset_7b3e9c1a5d2f8460",
        "position": 0,
        "identifier_value": "SPN-1"
      },
      "supplier": {
        "id": "sup_7c2e9a4f1b8d3506",
        "name": "Nordvolt Cells",
        "status": "linked"
      },
      "customer_team": { "id": 4821, "name": "Kestrel Energy" },
      "supplier_team": { "id": 5107, "name": "Nordvolt Cells AB" },
      "requested_by": { "id": 9031, "name": "Mara Lindqvist" },
      "submitted_by": { "id": 11208, "name": "Erna Kuvalis" },
      "reviewed_by": null,
      "applied_by": null,
      "cancelled_by": null,
      "source_passport": "pass_8d2f6a1c4e9b3057",
      "counts": { "total": 2, "accepted": 0, "rejected": 0, "applied": 0 },
      "created_at": "2026-09-20T09:13:02Z",
      "submitted_at": "2026-09-22T10:05:22Z",
      "reviewed_at": null,
      "applied_at": null,
      "cancelled_at": null,
      "updated_at": "2026-09-22T10:05:22Z"
    }
  ],
  "has_more": true,
  "next_cursor": "c_9f3a2b1c8d7e6f50",
  "url": "/v1/data_request_sets/dset_7b3e9c1a5d2f8460/children"
}

GET/v1/data_request_sets/:id/jobs/:job

Retrieve a job

Retrieves one bulk job of the set — the row a 202 answered with, or the set's create_job — from either side. Poll it until status is done or failed; progress moves while it runs and result fills in at the end. See jobs for what each kind reports. A job is looked up under its set only: one that is not this set's is 404 resource_missing, whoever started it. Requires data_requests:read.

Request

GET
/v1/data_request_sets/dset_7b3e9c1a5d2f8460/jobs/dsj_9c2e4a7b1d8f3056
curl https://api.synexcloud.com/v1/data_request_sets/dset_7b3e9c1a5d2f8460/jobs/dsj_9c2e4a7b1d8f3056 \
  -H "Authorization: Bearer $SYNEX_API_KEY"

Response

{
  "object": "data_request_set_job",
  "id": "dsj_9c2e4a7b1d8f3056",
  "set": "dset_7b3e9c1a5d2f8460",
  "kind": "apply",
  "status": "done",
  "progress": {
    "done": 1,
    "total": 1,
    "failed": [],
    "applied": [{ "id": "dreq_5a1c8e3f9b2d7046", "label": "LOT-1" }],
    "returned": []
  },
  "result": { "applied": 1, "returned": 0, "failed": 0 },
  "started_by": { "id": 9031, "name": "Mara Lindqvist" },
  "created_at": "2026-09-23T08:00:00Z",
  "updated_at": "2026-09-23T08:00:04Z"
}

The Excel round trip

Either side can download the set as an .xlsx workbook; the supplier fills it in and uploads it back. The workbook is generated on request from the children as they stand, so it doubles as a report.

The Answers sheet has one row per child, in row order. Three identity columns come first — Passport (the passport's label), Passport ID (its pass_ id) and Identifier (Friendly ID), Identifier (Supplier part no.) or a bare Identifier (the value the customer marked) — then one column per requested property, headed by the property's name. Cells are prefilled with whatever has been drafted or submitted so far: tags joined with , , booleans as true or false, files by their filename.

PassportPassport IDIdentifier (Friendly ID)CapacitySupplier part no.
LOT-1pass_6b0d3f8a2c5e1794LOT-13000SPN-1
  • A multilingual property (short_text, long_text, tags, file) has its bare column in the set's default_language, plus a <Name> (DE)-style column for every other language a row already holds. Add more columns for other EU languages as you need them — (FR), [fr] and - fr markers are all read.
  • A measurement column holds the bare number; the unit is not in the heading.
  • When two columns would share a heading — two requested properties with the same name, or one named like an identity column — the property's id heads its column instead.

The Properties sheet lists each requested property — its column, id, type, unit, whether it is required, and what a cell may hold — followed by the round-trip rules.

On upload, the sheet read is the one whose header has a Passport ID column, else the first. Then:

  • Rows are matched by Passport ID, never by their order. Keep that column as it is; Passport and Identifier are for reference and are not read.
  • Columns are matched by heading — a property's name or id, case-insensitively, with an optional language marker. An Error column is skipped too, so a corrected errors workbook can be uploaded as it is; any other heading is reported once as unknown_column and ignored.
  • An empty cell leaves the value alone. A cell holding only - clears it — the whole property in a bare column, one translation in a language column. A multilingual value merges per language: the columns you send replace the translations they name and leave the rest.
  • File and image cells take the filename of a file in your Drive (the only file of that name) or its file_… id.
  • Every cell is validated against the definitions pinned at create, as a submit is. One refused cell refuses its whole row and nothing of that row is drafted; the clean rows are.

GET/v1/data_request_sets/:id/export

Download the workbook

Returns the set as an .xlsx attachment, not JSON — see the Excel round trip for the layout. Either side. The attachment is named after the set: its level, its passport count and its id. Requires data_requests:read.

Request

GET
/v1/data_request_sets/dset_7b3e9c1a5d2f8460/export
curl https://api.synexcloud.com/v1/data_request_sets/dset_7b3e9c1a5d2f8460/export \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -o answers.xlsx

Response

HTTP/1.1 200 OK
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename="Cell x 3 (dset_7b3e9c1a5d2f8460).xlsx"
X-Request-Id: req_9f2c1ab34d5e6f70

POST/v1/data_request_sets/:id/uploads

Upload an answers workbook

Sends the filled-in workbook back as multipart/form-data. The file is kept in your Drive — in a folder named after the set under /Data requests/ — and read by a queued import job: the answer is 202 with the upload, processing, whose job you poll (or poll the upload itself) until it is done or failed. The supplier's call. Requires data_requests:write. Accepts an optional Idempotency-Key header, whose fingerprint covers the file's name, size and content.

A set still creating takes no upload (409 data_request_set_creating), nor does a closed one (409 data_request_set_closed).

Required attributes

  • Name
    file
    Type
    string
    Description

    The workbook, sent as a binary multipart/form-data part — an .xlsx of at most 10 MB. Anything else is 400 parameter_invalid with param: "file".

Optional attributes

  • Name
    submit_on_upload
    Type
    boolean
    Description

    Submit every row that imports clean as soon as it is drafted (true or 1) — and, on an auto_apply set, apply it. A row whose submit is refused is reported as submit_refused. Defaults to false.

Request

POST
/v1/data_request_sets/dset_7b3e9c1a5d2f8460/uploads
curl https://api.synexcloud.com/v1/data_request_sets/dset_7b3e9c1a5d2f8460/uploads \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -H "Idempotency-Key: answers-2026-09-22" \
  -F "file=@answers.xlsx" \
  -F "submit_on_upload=1"

Response

{
  "object": "data_request_set_upload",
  "id": "dsu_4e1b7d9a2c6f8035",
  "set": "dset_7b3e9c1a5d2f8460",
  "job": "dsj_3a8c5e1f7b2d9064",
  "status": "processing",
  "submit_on_upload": true,
  "file": "file_2d9f7a1c4e8b3056",
  "errors_file": null,
  "summary": null,
  "errors": [],
  "uploaded_by": { "id": 11208, "name": "Erna Kuvalis" },
  "created_at": "2026-09-22T10:02:11Z",
  "updated_at": "2026-09-22T10:02:11Z"
}

GET/v1/data_request_sets/:id/uploads/:upload

Retrieve an upload

Retrieves one upload of the set, from either side, as the import left it: summary counts the rows and errors says which cells were refused and why. file lives in the supplier's Drive — the supplier's key downloads it with GET /v1/files/:file/download; the customer reads errors here and has no download. An upload that is not this set's is 404 resource_missing. Requires data_requests:read.

Error codes

An entry's code is the value validator's own — invalid_number, invalid_measurement, invalid_boolean, value_too_large and the like, exactly as a direct passport edit reports them — or one of the importer's:

  • Name
    passport_id_column_missing
    Type
    code
    Description

    The sheet has no Passport ID column, so no row could be matched. Nothing was read.

  • Name
    unknown_column
    Type
    code
    Description

    A heading that is none of the requested properties, or a language column on a property with no translations. Reported on row 1; the column is ignored and the rows are not refused for it.

  • Name
    unknown_passport
    Type
    code
    Description

    An empty Passport ID cell, or an id that is not a passport of this set.

  • Name
    duplicate_row
    Type
    code
    Description

    A second row for the same passport. Only its first row was read.

  • Name
    row_closed
    Type
    code
    Description

    The row's child is no longer open to answers — submitted, applied or cancelled.

  • Name
    invalid_file
    Type
    code
    Description

    A file or image cell that names no single file of your Drive.

  • Name
    submit_refused
    Type
    code
    Description

    Under submit_on_upload, the row drafted but its submit was refused — a required field still blank, say. The draft stays.

Request

GET
/v1/data_request_sets/dset_7b3e9c1a5d2f8460/uploads/dsu_4e1b7d9a2c6f8035
curl https://api.synexcloud.com/v1/data_request_sets/dset_7b3e9c1a5d2f8460/uploads/dsu_4e1b7d9a2c6f8035 \
  -H "Authorization: Bearer $SYNEX_API_KEY"

Response

{
  "object": "data_request_set_upload",
  "id": "dsu_4e1b7d9a2c6f8035",
  "set": "dset_7b3e9c1a5d2f8460",
  "job": "dsj_3a8c5e1f7b2d9064",
  "status": "done",
  "submit_on_upload": true,
  "file": "file_2d9f7a1c4e8b3056",
  "errors_file": null,
  "summary": {
    "rows": 3,
    "valid": 2,
    "invalid": 1,
    "submitted": 2,
    "sheet": 0
  },
  "errors": [
    {
      "row": 2,
      "column": "Capacity",
      "property_id": "property_capacity",
      "code": "invalid_measurement",
      "message": "Expected a numeric measurement.",
      "passport": "pass_6b0d3f8a2c5e1794"
    }
  ],
  "uploaded_by": { "id": 11208, "name": "Erna Kuvalis" },
  "created_at": "2026-09-22T10:02:11Z",
  "updated_at": "2026-09-22T10:02:39Z"
}

Fulfilling from your own passports

When the customer marked an identifier, the supplier can pair the set's rows with its own passports and let Synex draft the answers from them — the bulk form of a single request's prefill. Three calls, all the supplier's:

  1. GET …/fulfil/sources — your ontologies, their levels and the properties that carry a value: where a match can look.
  2. POST …/fulfil/match — which level to read and which property holds the customer's identifier value. Each row comes back matched, ambiguous or unmatched. Nothing is stored.
  3. POST …/fulfil/fill — the pairs you accept: the matched rows as returned, the others paired by hand with any passport of yours. A queued job drafts each child from its source.

Nothing is submitted. Review the drafts, then submit every child. A set with no identifier cannot be matched (409 identifier_missing) — pair its rows by hand and go straight to fill.


GET/v1/data_request_sets/:id/fulfil/sources

List match sources

Lists your ontologies with their levels and, per level, the properties that carry a value — the pick list for a match. It covers the first 50 of your ontologies, as GET /v1/ontologies lists them, so an ontology-scoped ontologies:read narrows it the same way. A fixed list, never paged. The supplier's call. Requires data_requests:write.

Request

GET
/v1/data_request_sets/dset_7b3e9c1a5d2f8460/fulfil/sources
curl https://api.synexcloud.com/v1/data_request_sets/dset_7b3e9c1a5d2f8460/fulfil/sources \
  -H "Authorization: Bearer $SYNEX_API_KEY"

Response

{
  "object": "list",
  "data": [
    {
      "id": "ontology_9b2d7e4f1a8c3065",
      "name": "Supplier cells",
      "default_language": "en",
      "levels": [
        {
          "key": "S",
          "name": "Pack",
          "properties": [
            { "id": "property_pack_name", "name": "Pack name", "type": "short_text" }
          ]
        },
        {
          "key": "T",
          "name": "Cell",
          "properties": [
            { "id": "property_t1", "name": "Supplier part no.", "type": "short_text" },
            { "id": "property_t2", "name": "Capacity", "type": "measurement" }
          ]
        }
      ]
    }
  ],
  "has_more": false,
  "next_cursor": null,
  "url": "/v1/data_request_sets/dset_7b3e9c1a5d2f8460/fulfil/sources"
}

POST/v1/data_request_sets/:id/fulfil/match

Match rows to your passports

Pairs every child still open to answers (open or changes_requested), in row order, with the passport of yours that carries the same identifier value, at one level of one of your ontologies. Exactly one passport is matched (in source, and alone in candidates), several are ambiguous (all in candidates, source null), none is unmatched. Values compare trimmed and case-insensitively; a language map by every translation it holds, a list by each element, a number by its value however it is written — "3000", 3000 and 3000.0 meet. Nothing is stored; feed the pairs you accept to fill. The supplier's call. Requires data_requests:write, plus passports:read covering the chosen ontology.

A set the customer marked no identifier on is 409 identifier_missing; an ontology that is not yours is 404 resource_missing; a level or property it does not have is 400 parameter_invalid.

Required attributes

  • Name
    ontology
    Type
    string
    Description

    The ontology_ id of one of your ontologies.

  • Name
    depth
    Type
    string
    Description

    The level key whose passports are read.

  • Name
    property
    Type
    string
    Description

    A property id of that level holding the customer's identifier value, or friendly_id to match on your friendly ids.

Request

POST
/v1/data_request_sets/dset_7b3e9c1a5d2f8460/fulfil/match
curl https://api.synexcloud.com/v1/data_request_sets/dset_7b3e9c1a5d2f8460/fulfil/match \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ontology": "ontology_9b2d7e4f1a8c3065",
    "depth": "T",
    "property": "property_t1"
  }'

Response

{
  "source": {
    "ontology": "ontology_9b2d7e4f1a8c3065",
    "depth": "T",
    "property": "property_t1"
  },
  "passports_read": 412,
  "truncated": false,
  "rows": [
    {
      "request_id": "dreq_5a1c8e3f9b2d7046",
      "passport_label": "LOT-1",
      "identifier_value": "SPN-1",
      "status": "matched",
      "source": { "snowflake": "pass_8d2f6a1c4e9b3057", "friendly_id": "S-1" },
      "candidates": [
        { "snowflake": "pass_8d2f6a1c4e9b3057", "friendly_id": "S-1" }
      ]
    },
    {
      "request_id": "dreq_6b2d9f4a0c3e8157",
      "passport_label": "LOT-2",
      "identifier_value": "SPN-2",
      "status": "ambiguous",
      "source": null,
      "candidates": [
        { "snowflake": "pass_1c4e8b3a7d2f9065", "friendly_id": "S-2" },
        { "snowflake": "pass_2d5f9c4b8e3a0176", "friendly_id": "S-3" }
      ]
    },
    {
      "request_id": "dreq_7c3e0a5b1d4f9268",
      "passport_label": "LOT-3",
      "identifier_value": null,
      "status": "unmatched",
      "source": null,
      "candidates": []
    }
  ]
}

POST/v1/data_request_sets/:id/fulfil/fill

Draft children from your passports

Drafts the paired children from the named passports of yours, as one queued fill job: 202 with the job to poll. Per pair it is the single request's prefill — properties paired by name, values converted where the types allow, the source recorded on each value — saved as that child's draft. A field that already holds a value is left alone unless replace is true. Nothing is submitted; follow with submit_all or the children's own submit. The supplier's call. Requires data_requests:write, plus passports:read covering each source's ontology. Accepts an optional Idempotency-Key header.

A closed set is 409 data_request_set_closed. A pair naming a child that is not the set's, a passport that is not yours, or a child no longer open to answers fails that pair alone, in progress.failed.

Required attributes

  • Name
    pairs
    Type
    array
    Description

    1–500 entries, each a request_id (a child's dreq_ id, from children or a match row) and a source_snowflake (the pass_ id of one of your passports). A child named twice takes the last source given.

Optional attributes

  • Name
    replace
    Type
    boolean
    Description

    Overwrite fields that already hold a value. Defaults to false.

Request

POST
/v1/data_request_sets/dset_7b3e9c1a5d2f8460/fulfil/fill
curl https://api.synexcloud.com/v1/data_request_sets/dset_7b3e9c1a5d2f8460/fulfil/fill \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "pairs": [
      { "request_id": "dreq_5a1c8e3f9b2d7046", "source_snowflake": "pass_8d2f6a1c4e9b3057" },
      { "request_id": "dreq_6b2d9f4a0c3e8157", "source_snowflake": "pass_1c4e8b3a7d2f9065" }
    ],
    "replace": false
  }'

Response

{
  "object": "data_request_set_job",
  "id": "dsj_5d8f2a7c1e4b9063",
  "set": "dset_7b3e9c1a5d2f8460",
  "kind": "fill",
  "status": "queued",
  "progress": { "done": 0, "total": 2, "failed": [], "filled": [] },
  "result": null,
  "started_by": { "id": 11208, "name": "Erna Kuvalis" },
  "created_at": "2026-09-22T11:15:00Z",
  "updated_at": "2026-09-22T11:15:00Z"
}

POST/v1/data_request_sets/:id/submit_all

Submit every child

Submits every open or changes_requested child with whatever it holds — drafted by hand, by an upload or by a fill — as one queued submit job: 202 with the job to poll. Each child is submitted exactly as a single submit would be, validated against the definitions pinned at create: a child that is refused is left as it was and listed in progress.failed with the code; the rest go submitted — or applied at once on an auto_apply set (progress.applied), or stay submitted with apply_blocked_reason when the passport refused the automatic apply (progress.blocked). The supplier's call. Requires data_requests:write. Accepts an optional Idempotency-Key header.

A closed set is 409 data_request_set_closed; one with no child left to answer is 409 nothing_to_submit. Once anything was submitted, one data_request_set.submitted fires on the customer's feed.

Request

POST
/v1/data_request_sets/dset_7b3e9c1a5d2f8460/submit_all
curl -X POST https://api.synexcloud.com/v1/data_request_sets/dset_7b3e9c1a5d2f8460/submit_all \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -H "Idempotency-Key: submit-dset_7b3e9c1a5d2f8460"

Response

{
  "object": "data_request_set_job",
  "id": "dsj_2b7d1e9c4a6f8035",
  "set": "dset_7b3e9c1a5d2f8460",
  "kind": "submit",
  "status": "queued",
  "progress": {
    "done": 0,
    "total": 3,
    "failed": [],
    "submitted": [],
    "applied": [],
    "blocked": []
  },
  "result": null,
  "started_by": { "id": 11208, "name": "Erna Kuvalis" },
  "created_at": "2026-09-22T10:00:00Z",
  "updated_at": "2026-09-22T10:00:00Z"
}

POST/v1/data_request_sets/:id/apply_all

Accept and apply every child

Accepts every value still awaiting a verdict on every submitted child and applies the child, as one queued apply job: 202 with the job to poll — the bulk form of a single request's review and apply with every verdict accepted. A value you rejected by hand stays rejected, and that child goes back to changes_requested for the supplier to revise (progress.returned); the rest apply (progress.applied). A child whose apply is refused — passport_released, team_suspended, ontology_locked, a passports:write scope that does not cover it — is left exactly as it was, still submitted, and listed in progress.failed with the code. To return one child or reject single values, use the child's own review on /v1/data_requests/:id. The customer's call. Requires data_requests:write and passports:write covering the passports' ontology — 403 ability_missing without it. Accepts an optional Idempotency-Key header.

A closed set is 409 data_request_set_closed; one with no submission waiting is 409 nothing_to_apply. Once anything applied, one data_request_set.applied fires on each team's feed.

Request

POST
/v1/data_request_sets/dset_7b3e9c1a5d2f8460/apply_all
curl -X POST https://api.synexcloud.com/v1/data_request_sets/dset_7b3e9c1a5d2f8460/apply_all \
  -H "Authorization: Bearer $SYNEX_API_KEY" \
  -H "Idempotency-Key: apply-dset_7b3e9c1a5d2f8460"

Response

{
  "object": "data_request_set_job",
  "id": "dsj_9c2e4a7b1d8f3056",
  "set": "dset_7b3e9c1a5d2f8460",
  "kind": "apply",
  "status": "queued",
  "progress": {
    "done": 0,
    "total": 1,
    "failed": [],
    "applied": [],
    "returned": []
  },
  "result": null,
  "started_by": { "id": 9031, "name": "Mara Lindqvist" },
  "created_at": "2026-09-23T08:00:00Z",
  "updated_at": "2026-09-23T08:00:00Z"
}

POST/v1/data_request_sets/:id/cancel

Cancel a bulk data request

Takes the whole set back. The set closes first, so a create job still running makes no more children; then every child not yet terminal is cancelled with closed_reason: "by_customer", each emitting its own data_request.cancelled. Values a child had already applied stay on its passport. Returns the closed set. The customer's call. Requires data_requests:write. Accepts an optional Idempotency-Key header.

A set already closed is 409 data_request_set_closed. data_request_set.cancelled fires on your feed and, when a supplier team holds the set, on theirs.

Request

POST
/v1/data_request_sets/dset_7b3e9c1a5d2f8460/cancel
curl -X POST https://api.synexcloud.com/v1/data_request_sets/dset_7b3e9c1a5d2f8460/cancel \
  -H "Authorization: Bearer $SYNEX_API_KEY"

Response

{
  "object": "data_request_set",
  "id": "dset_7b3e9c1a5d2f8460",
  "status": "closed",
  "closed_reason": "cancelled",
  "passport_count": 3,
  "depth": { "key": "B", "name": "Cell" },
  "ontology": {
    "id": "ontology_00a1b2c3d4e5f607",
    "version": 3,
    "default_language": "en"
  },
  "properties": ["property_capacity", "property_part_no"],
  "definitions": [
    {
      "id": "property_capacity",
      "name": "Capacity",
      "type": "measurement",
      "value": { "measurement": "mAh" }
    },
    {
      "id": "property_part_no",
      "name": "Supplier part no.",
      "type": "short_text",
      "value": { "required": false }
    }
  ],
  "identifier": { "kind": "friendly_id" },
  "selection": { "mode": "list", "ids": ["LOT-1", "LOT-2", "LOT-3"] },
  "message": null,
  "auto_apply": false,
  "due_at": null,
  "supplier": {
    "id": "sup_7c2e9a4f1b8d3506",
    "name": "Nordvolt Cells",
    "status": "linked"
  },
  "customer_team": { "id": 4821, "name": "Kestrel Energy" },
  "supplier_team": { "id": 5107, "name": "Nordvolt Cells AB" },
  "requested_by": { "id": 9031, "name": "Mara Lindqvist" },
  "cancelled_by": { "id": 9031, "name": "Mara Lindqvist" },
  "counts": {
    "total": 3,
    "open": 0,
    "submitted": 0,
    "changes_requested": 0,
    "applied": 1,
    "cancelled": 2
  },
  "create_job": "dsj_1f4a8c2e6b9d3075",
  "progress": null,
  "created_at": "2026-09-20T09:12:40Z",
  "closed_at": "2026-09-25T16:20:08Z",
  "updated_at": "2026-09-25T16:20:08Z",
  "viewer_role": "customer"
}

Events

A set's own events carry the set, as that side sees it, in data.object.

TypeFires whenFeed
data_request_set.createdThe create job has made the children.Customer
data_request_set.receivedThe set reaches the supplier team — when the create job finishes over a linked supplier, or when the link's invitation is accepted.Supplier
data_request_set.submittedA submit_all, or an upload with submit_on_upload, submitted anything.Customer
data_request_set.appliedAn apply_all applied anything.Both
data_request_set.cancelledThe customer cancelled the set.Customer, and the supplier's when a supplier team holds the set

A child's creation is announced by the set alone: the create job emits no data_request.created or data_request.received per child. From then on the children are real requests with their own data_request.* events — each gets data_request.received when the supplier accepts a pending link's invitation, and each submit, apply and cancel emits as it would for a single request. An apply_all over 240 children produces 240 data_request.applied on each feed and one data_request_set.applied. Filter the events feed on object_id — the dset_ id for the set's own events, a dreq_ id for one child's.

Errors

codeStatusWhen
selection_invalid400A passport of the selection cannot be taken, or the selection names none. error.errors is keyed by the offending id. Nothing is opened.
selection_too_large400The selection covers more than 500 passports.
parameter_invalid400A malformed body or query; a level not below the parent; a property that is not at the level or carries no fillable value; an identifier property that carries no value; an upload that is not an .xlsx of at most 10 MB; an unknown depth or property on a match.
invalid_cursor400A cursor minted under another direction or filter, or a bare position on children.
ability_missing403Opening a set or apply_all without passports:write; param is selection.
ability_not_scoped_to_resource403A data_requests:* grant with an ontology scope, or a passports:write / passports:read scope that does not cover the ontology.
team_suspended403Your team is suspended.
resource_missing404The set, upload or job is not your team's, your team is on the wrong side for the call, or the supplier or the match ontology is not yours.
link_revoked409The supplier link has been revoked.
supplier_already_linked409The recipient address is already linked or invited — name its supplier instead.
data_request_set_creating409An upload on a set whose children are not made yet.
data_request_set_closed409A cancel, submit_all, apply_all, upload or fill on a closed set.
nothing_to_submit409submit_all with no child left to answer.
nothing_to_apply409apply_all with no submission waiting.
identifier_missing409fulfil/match on a set the customer marked no identifier on.

See errors for the envelope and handling guidance.

Was this page helpful?