Supplier requests and replies
Base URL: https://app.trymaglev.com/api/v1. All endpoint families.
GET /rfqs
Section titled “GET /rfqs”The managed supplier requests linked to your organization’s managed projects, newest first.
Read only, and owner scoped to the buyer organization on the production key. A supplier organization key is refused with 403 supplier_audience_forbidden, because this projection carries the Maglev fee line and the buyer side of an anonymised request. Sandbox keys carry no organization and receive 404. An RFQ appears here whether it was published through POST /actions/{id}/publication or created in the Maglev workspace; an agent that has only run quote_request, without publishing it, will see an empty list. At most 100 records; truncated says whether more exist.
Required scope: quotes:write.
Authentication: bearer key.
| Parameter | Location | Required | Details |
|---|---|---|---|
projectId |
query | No | Managed project uuid. A project owned by another organization simply matches nothing. |
200 response schema
{ "type": "object", "properties": { "audience": { "type": "string", "const": "buyer" }, "rfqs": { "type": "array", "items": { "$ref": "#/components/schemas/RfqSummary", "description": "With invitationCount, declinedCount, respondedCount and submittedQuoteCount added" } }, "count": { "type": "integer" }, "truncated": { "type": "boolean" }, "extractedEvidence": { "type": "object", "properties": { "included": { "type": "boolean", "const": false }, "note": { "type": "string" } }, "required": [ "included", "note" ] }, "note": { "type": "string" }, "requestId": { "type": "string" } }, "required": [ "audience", "rfqs", "count", "truncated", "extractedEvidence", "note", "requestId" ]}Errors: 400 Validation error; 401 Missing or invalid key; 403 Scope or mode does not allow this; 404 Not found; 429 Rate limit reached. See recovery guidance.
GET /rfqs/{id}
Section titled “GET /rfqs/{id}”One managed supplier request with its per-factory invitations and the supplier quote versions actually linked to it.
Owner scoped: an RFQ whose managed project belongs to another organization returns 404, and a supplier organization key returns 403 before any read. Absent values stay null or unknown; a workspace-created RFQ whose currency or Incoterm is still at its column default is flagged rather than reported as declared, while an RFQ published through the API stated both explicitly and is not flagged, and a missing validUntil is validity state unknown rather than valid. A quote still in draft has not been submitted and stays supplier-private, the same rule the workspace quote board applies. Declines are returned as invitations with status declined and their reason. Model-extracted quote_record output is draft evidence on the action and is never merged in here. A request published through the API can be corrected afterwards, and a correction is a new revision on the same RFQ and the same invitations rather than a second request; rfq.requested reports the current revision, revisions lists every one that was published and to whom, and each quote says which revision it answers and whether a later one superseded it.
Required scope: quotes:write.
Authentication: bearer key.
| Parameter | Location | Required | Details |
|---|---|---|---|
id |
path | Yes | Managed RFQ uuid |
200 response schema
{ "type": "object", "properties": { "audience": { "type": "string", "const": "buyer" }, "rfq": { "$ref": "#/components/schemas/RfqSummary" }, "revisions": { "type": "array", "items": { "type": "object", "properties": { "revision": { "type": "integer" }, "reviewSha256": { "type": "string" }, "publishedAt": { "type": "string", "format": "date-time" }, "factoryIds": { "type": "array", "items": { "type": "string", "format": "uuid" } } }, "required": [ "revision", "reviewSha256", "publishedAt", "factoryIds" ] }, "description": "Every reviewed revision of this request that was published, oldest first, with the recipients each one went to. Append only: correcting a published request adds a revision on the same RFQ and the same invitations and never rewrites an earlier one, so this is the record of what was disclosed, to whom and when. Empty for an RFQ created in the Maglev workspace." }, "currentRevision": { "anyOf": [ { "type": "integer", "description": "The newest published revision, whose terms rfq.requested reports; null when this RFQ was not published through the API" }, { "type": "null" } ] }, "invitations": { "type": "array", "items": { "$ref": "#/components/schemas/RfqInvitation" } }, "counts": { "type": "object", "properties": { "invitations": { "type": "integer" }, "declined": { "type": "integer" }, "quoted": { "type": "integer" }, "accepted": { "type": "integer" }, "awaitingResponse": { "type": "integer" }, "submittedQuotes": { "type": "integer" } }, "required": [ "invitations", "declined", "quoted", "accepted", "awaitingResponse", "submittedQuotes" ] }, "extractedEvidence": { "type": "object", "properties": { "included": { "type": "boolean", "const": false }, "note": { "type": "string" } }, "required": [ "included", "note" ] }, "note": { "type": "string" }, "requestId": { "type": "string" } }, "required": [ "audience", "rfq", "revisions", "currentRevision", "invitations", "counts", "extractedEvidence", "note", "requestId" ]}Errors: 400 Validation error; 401 Missing or invalid key; 403 Scope or mode does not allow this; 404 Not found; 429 Rate limit reached. See recovery guidance.
GET /pending-disclosures
Section titled “GET /pending-disclosures”Read current and historical supplier file disclosures for your organization
Read only, and owner scoped exactly as GET /rfqs: the managed project’s organization on the production key, with a supplier organization refused 403 before any read and a sandbox key 404. Sending files to a directory factory with no bound supplier account grants nothing at that moment and grants the whole package the moment a Maglev operator binds an account to that factory, which is not your action and can be weeks later. Each row here is one of those outstanding promises. state pending means it has been granted to nobody yet and will be granted in full, with no second approval, if an account is bound before expiresAt. state delivered means it has already been granted and the supplier organization can read those files now. state expired or revoked grants nothing on a later binding, and republishing the same reviewed revision does not revive it. This route reads; it delivers and revokes nothing. Source audit: this list also includes recipients already bound when the request was published. The pending state is a future handover; delivered records are the revocation handles for access already granted. Counts describe the returned bounded list; inspect truncated before assuming complete coverage. Expiry stops a pending handover but does not revoke access already delivered.
Required scope: quotes:write.
Authentication: bearer key.
| Parameter | Location | Required | Details |
|---|---|---|---|
projectId |
query | No | Managed project uuid. A project owned by another organization simply matches nothing.. Format: uuid |
rfqId |
query | No | Managed RFQ uuid.. Format: uuid |
state |
query | No | One of pending, delivered, expired, revoked. |
200 response schema
{ "type": "object", "properties": { "audience": { "type": "string", "const": "buyer" }, "disclosures": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", "description": "uuid", "format": "uuid" }, "rfqId": { "type": "string", "description": "uuid", "format": "uuid" }, "projectId": { "type": "string", "description": "uuid", "format": "uuid" }, "factoryId": { "type": "string", "description": "uuid", "format": "uuid" }, "factoryNameEn": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "factoryNameZh": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "fileIds": { "type": "array", "items": { "type": "string", "description": "uuid", "format": "uuid" }, "description": "The reviewed files this disclosure covers" }, "fileCount": { "type": "integer" }, "state": { "type": "string", "enum": [ "pending", "delivered", "expired", "revoked" ] }, "createdAt": { "type": "string", "format": "date-time" }, "expiresAt": { "type": "string", "description": "When the promise lapses. A supplier account bound after this instant is granted nothing", "format": "date-time" }, "deliveredAt": { "anyOf": [ { "type": "string", "description": "When a supplier account was bound and the package was actually granted", "format": "date-time" }, { "type": "null" } ] }, "deliveredFileIds": { "type": "array", "items": { "type": "string", "description": "uuid", "format": "uuid" }, "description": "What binding actually granted, which is the current disclosure record that the publication receipt's grantedFileIds is not" }, "revokedAt": { "anyOf": [ { "type": "string", "format": "date-time" }, { "type": "null" } ] }, "revokedBy": { "anyOf": [ { "type": "string", "description": "uuid", "format": "uuid" }, { "type": "null" } ] }, "revokedReason": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "revokedGrantFileIds": { "type": "array", "items": { "type": "string", "description": "uuid", "format": "uuid" }, "description": "Grants that already existed when this was revoked and were deleted. Deleting them stops further reads; it cannot unsend a file that was already downloaded" } }, "required": [ "id", "rfqId", "projectId", "factoryId", "factoryNameEn", "factoryNameZh", "fileIds", "fileCount", "state", "createdAt", "expiresAt", "deliveredAt", "deliveredFileIds", "revokedAt", "revokedBy", "revokedReason", "revokedGrantFileIds" ] } }, "counts": { "type": "object", "properties": { "pending": { "type": "integer" }, "delivered": { "type": "integer" }, "expired": { "type": "integer" }, "revoked": { "type": "integer" } }, "required": [ "pending", "delivered", "expired", "revoked" ] }, "windowDays": { "type": "integer", "description": "How long a new pending disclosure stays deliverable" }, "truncated": { "type": "boolean" }, "note": { "type": "string" }, "requestId": { "type": "string" } }, "required": [ "audience", "disclosures", "counts", "windowDays", "truncated", "note", "requestId" ]}Errors: 400 Validation error; 401 Missing or invalid key; 403 Scope or mode does not allow this; 404 Not found; 429 Rate limit reached. See recovery guidance.
POST /pending-disclosures/{id}/revoke
Section titled “POST /pending-disclosures/{id}/revoke”Take one outstanding engineering package back, and delete any grant it already caused.
What the GET’s note tells you to do. Owner scoped exactly as GET /pending-disclosures, and it additionally needs the person: revocation is recorded against a named user on the row and in the audit log, an API key names an organization and not a person, so this call requires the production key AND a Maglev browser session in the same organization. A key alone answers 401 sign_in_required and a session in another organization answers 403; neither is a way to revoke on someone else’s behalf. A disclosure belonging to another organization answers 404 with the same sentence as one that does not exist, so a guessed id tells you nothing. The body is JSON and its only field is an optional reason, which is copied onto the row and into the audit entry; unknown fields are rejected rather than dropped, because a misspelled one would read afterwards as a founder who gave no reason. Revoking is idempotent: a second call returns 200 with alreadyRevoked true, the first revocation’s timestamp, and no second audit entry. File access is granted per supplier organization rather than per directory factory, so this reaches every other live disclosure of this same request to that organization; those are listed in cascaded and receive nothing from a later revision either. It is honest about its limits and the response says so in full: revokedGrants is what was actually deleted, retainedGrants is what was deliberately left alone because a disclosure outside this request still authorizes that organization to read that file and must be revoked separately, and openedBefore lists the reads Maglev recorded for these files by that organization before the grants went away. That last one is a floor on what was seen and never a proof that nothing was: revoking stops future reads and cannot retrieve or erase a copy that has already left Maglev.
Required scope: quotes:write.
Authentication: bearer key + browser session.
| Parameter | Location | Required | Details |
|---|---|---|---|
id |
path | Yes | Pending disclosure uuid, as returned by GET /pending-disclosures or as pendingDisclosureId on a publication receipt. Format: uuid |
| Field | Type | Required | Details |
|---|---|---|---|
reason |
string | No | Optional reason, trimmed before validation and recorded with the revocation.. Min length 1. Max length 2000 |
200 response schema
{ "type": "object", "properties": { "audience": { "type": "string", "const": "buyer" }, "disclosure": { "type": "object", "properties": { "id": { "type": "string", "description": "uuid", "format": "uuid" }, "rfqId": { "type": "string", "description": "uuid", "format": "uuid" }, "projectId": { "type": "string", "description": "uuid", "format": "uuid" }, "factoryId": { "type": "string", "description": "uuid", "format": "uuid" }, "factoryNameEn": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "factoryNameZh": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "fileIds": { "type": "array", "items": { "type": "string", "description": "uuid", "format": "uuid" } }, "fileCount": { "type": "integer" }, "state": { "type": "string", "enum": [ "pending", "delivered", "expired", "revoked" ] }, "createdAt": { "type": "string", "format": "date-time" }, "expiresAt": { "type": "string", "format": "date-time" }, "deliveredAt": { "anyOf": [ { "type": "string", "format": "date-time" }, { "type": "null" } ] }, "deliveredFileIds": { "type": "array", "items": { "type": "string", "description": "uuid", "format": "uuid" } }, "revokedAt": { "anyOf": [ { "type": "string", "format": "date-time" }, { "type": "null" } ] }, "revokedBy": { "anyOf": [ { "type": "string", "description": "uuid", "format": "uuid" }, { "type": "null" } ] }, "revokedReason": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }, "revokedGrantFileIds": { "type": "array", "items": { "type": "string", "description": "uuid", "format": "uuid" } } }, "required": [ "id", "rfqId", "projectId", "factoryId", "factoryNameEn", "factoryNameZh", "fileIds", "fileCount", "state", "createdAt", "expiresAt", "deliveredAt", "deliveredFileIds", "revokedAt", "revokedBy", "revokedReason", "revokedGrantFileIds" ] }, "revokedGrants": { "type": "array", "items": { "type": "object", "properties": { "fileId": { "type": "string", "description": "uuid", "format": "uuid" }, "organisationId": { "type": "string", "description": "uuid", "format": "uuid" } }, "required": [ "fileId", "organisationId" ] }, "description": "The file grants this call deleted. Empty when the package had not been delivered yet, which is the whole point of revoking early" }, "cascaded": { "type": "array", "items": { "type": "object", "properties": { "disclosureId": { "type": "string", "description": "uuid", "format": "uuid" }, "factoryId": { "type": "string", "description": "uuid", "format": "uuid" }, "fileIds": { "type": "array", "items": { "type": "string", "description": "uuid", "format": "uuid" } } }, "required": [ "disclosureId", "factoryId", "fileIds" ] }, "description": "Other live disclosures of this same request to the same supplier organization, revoked with this one because access is per organization" }, "retainedGrants": { "type": "array", "items": { "type": "object", "properties": { "fileId": { "type": "string", "description": "uuid", "format": "uuid" }, "organisationId": { "type": "string", "description": "uuid", "format": "uuid" }, "authorisedByDisclosureId": { "type": "string", "description": "uuid", "format": "uuid" }, "authorisedByRfqId": { "type": "string", "description": "uuid", "format": "uuid" }, "authorisedByFactoryId": { "type": "string", "description": "uuid", "format": "uuid" } }, "required": [ "fileId", "organisationId", "authorisedByDisclosureId", "authorisedByRfqId", "authorisedByFactoryId" ] }, "description": "Grants that were NOT deleted, each named with the live disclosure outside this request that still authorizes it. Revoke those to take the file back" }, "alreadyRevoked": { "type": "boolean", "description": "True when this disclosure was already revoked. The call is idempotent: the first revocation stands, nothing is deleted twice and no second audit entry is written" }, "openedBefore": { "type": "array", "items": { "type": "object", "properties": { "fileId": { "type": "string", "description": "uuid", "format": "uuid" }, "at": { "type": "string", "format": "date-time" }, "actorUserId": { "anyOf": [ { "type": "string", "description": "uuid", "format": "uuid" }, { "type": "null" } ] } }, "required": [ "fileId", "at", "actorUserId" ] }, "description": "Reads Maglev recorded for these files by that organization before the grants were deleted. It records opens through the Maglev file route only, so it is a floor on what was seen, never a proof that nothing was" }, "note": { "type": "string", "description": "What revocation is and is not, in full" }, "requestId": { "type": "string" } }, "required": [ "audience", "disclosure", "revokedGrants", "cascaded", "retainedGrants", "alreadyRevoked", "openedBefore", "note", "requestId" ]}Errors: 400 Validation error; 401 Missing or invalid key; 403 Scope or mode does not allow this; 404 Not found; 429 Rate limit reached. See recovery guidance.