curl --request POST \
--url https://api.usenexio.com/api/v1/conversation-instances/{instance_slug}/conversations/{conversation_id}/attachments/reserve \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"end_user": "u_dana_ortiz",
"filename": "statement-of-values.xlsx",
"content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"size_bytes": 18874368
}
'import requests
url = "https://api.usenexio.com/api/v1/conversation-instances/{instance_slug}/conversations/{conversation_id}/attachments/reserve"
payload = {
"end_user": "u_dana_ortiz",
"filename": "statement-of-values.xlsx",
"content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"size_bytes": 18874368
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
end_user: 'u_dana_ortiz',
filename: 'statement-of-values.xlsx',
content_type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
size_bytes: 18874368
})
};
fetch('https://api.usenexio.com/api/v1/conversation-instances/{instance_slug}/conversations/{conversation_id}/attachments/reserve', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"attachment": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"filename": "<string>",
"media_type": "<string>",
"size_bytes": 123,
"status": "storing",
"delivery": "file",
"created_at": "2023-11-07T05:31:56Z",
"notice": "<string>",
"error": "<string>",
"container_kind": "zip",
"parent_attachment_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"expires_at": "2023-11-07T05:31:56Z"
},
"upload_url": "<string>",
"upload_url_expires_at": "2023-11-07T05:31:56Z",
"source_path": "<string>"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "unauthorized",
"message": "Missing or invalid API key"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "rate_limited",
"message": "Rate limit exceeded"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "attachments_unavailable",
"message": "File attachments are not available in this environment."
}{
"code": "request_timeout",
"message": "Request timed out"
}Reserve an attachment
Open an attachment record and get a presigned URL to PUT one file’s bytes to.
curl --request POST \
--url https://api.usenexio.com/api/v1/conversation-instances/{instance_slug}/conversations/{conversation_id}/attachments/reserve \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"end_user": "u_dana_ortiz",
"filename": "statement-of-values.xlsx",
"content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"size_bytes": 18874368
}
'import requests
url = "https://api.usenexio.com/api/v1/conversation-instances/{instance_slug}/conversations/{conversation_id}/attachments/reserve"
payload = {
"end_user": "u_dana_ortiz",
"filename": "statement-of-values.xlsx",
"content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"size_bytes": 18874368
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
end_user: 'u_dana_ortiz',
filename: 'statement-of-values.xlsx',
content_type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
size_bytes: 18874368
})
};
fetch('https://api.usenexio.com/api/v1/conversation-instances/{instance_slug}/conversations/{conversation_id}/attachments/reserve', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"attachment": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"filename": "<string>",
"media_type": "<string>",
"size_bytes": 123,
"status": "storing",
"delivery": "file",
"created_at": "2023-11-07T05:31:56Z",
"notice": "<string>",
"error": "<string>",
"container_kind": "zip",
"parent_attachment_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"expires_at": "2023-11-07T05:31:56Z"
},
"upload_url": "<string>",
"upload_url_expires_at": "2023-11-07T05:31:56Z",
"source_path": "<string>"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "unauthorized",
"message": "Missing or invalid API key"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "rate_limited",
"message": "Rate limit exceeded"
}{
"code": "<string>",
"message": "<string>",
"details": "<unknown>"
}{
"code": "attachments_unavailable",
"message": "File attachments are not available in this environment."
}{
"code": "request_timeout",
"message": "Request timed out"
}Behavior
Reserve is step one of three. PUT the file’s bytes toupload_url (no Authorization header; the URL expires in 15 minutes and can write only this one file), then call finalize. The record stays storing until finalize and becomes eligible for removal one hour after reserve if never finalized; the daily cleanup pass removes it. See Attachments.Authorizations
Send the key as Authorization: Bearer <key>. Two kinds of key exist.
Organization keys are issued in the portal (Settings, then API keys),
each bound to one environment, shaped nx_<environment slug>_<64 hex>. They carry
no capabilities and pass every capability check, with one exception:
routes under /api/v1/records, /api/v1/engines/{id}/opportunities,
/api/v1/graph and /api/v1/catalog/documents accept an
organization key only when its environment is live, and refuse any
other with 403 scoped_key_required. Revocation takes effect within 60
seconds.
Scoped keys are issued by Nexio on request, shaped
nxsk_v1_<24 hex key id>_<43 character secret>. Each is bound to one
org, one environment, a set of engines and a set of capabilities. A
malformed, unknown or revoked nxsk_ key fails with 401 and is never
retried as an organization key. Revocation takes effect on the next
request. A scoped key without a route's capability gets 403
insufficient_capability; a scoped key not bound to the engine gets 403
engine_binding_forbidden.
Key-grantable capabilities: engines:read, runs:write, runs:read,
runs:defensibility:read, runs:test, catalog:read,
catalog:documents:read, webhooks:manage, conversations:use,
conversations:export, records:read, records:opportunities:run,
actions:write, actions:read, graph:read, records:analyze.
Routes that accept organization keys only (every scoped key gets 403
insufficient_capability): environment management, engine create,
update, configuration and publish, and conversation instance
authoring. Each operation description names the capability a scoped
key needs.
Path Parameters
Conversation instance identifier slug (e.g. platform-assistant).
Body
The asserted end-user identity the conversation must belong to.
File name. May carry directories; each segment is cleaned and traversal segments are dropped. The extension decides the media type.
Declared size in bytes. Zero or less is refused as an empty file.
x >= 1Declared media type. Used only when the name has no extension.
Response
The reserved record and its upload URL.
One reserved attachment record plus the write grant for its bytes.
One file attached to a conversation. There is no text field: the platform does not parse the document, it hands the bytes to the model.
Hide child attributes
Hide child attributes
ready can ride a turn. A multipart upload answers ready. A
reserved upload is storing until finalize; a folder container
is unwrapping until every member is finalized. A finalize that
finds no bytes leaves the record storing. A finalize that refuses
the file for any other reason marks it rejected and removes it at
once, so it no longer lists or reads. failed is reserved and not
currently set. Only ready records can ride a turn or be served.
storing, unwrapping, ready, rejected, failed How the file reaches the model. file and image are provider
inputs. unwrap is a container, which never reaches the model
itself; a turn naming it carries the files inside it.
A folder container also has delivery unwrap.
file, image, unwrap How this format is read, when there is a limit worth knowing. On a zip or email container it says how many files were read and what was left out; on a folder container it says how many files the folder holds. A spreadsheet is read to the first 1,000 rows per sheet; text is read out of Office files while images and charts inside them are not. Absent when the file is read whole. Show it: an answer drawn from part of a schedule is not an answer about the schedule.
Why the record was rejected or failed. Absent otherwise.
What a container is. zip and eml are opened by the platform;
folder is opened by the client and its files uploaded one by
one. Set on a folder, and on a zip or email stored through
finalize. Absent on a zip or email stored by a multipart upload,
on a plain file, and on a file inside a container.
zip, eml, folder The container this file was found inside, when it was.
When access to the attachment ends. From this moment it is left
out of lists, answers 404 when read or downloaded, and cannot be
named on a turn. A later retention sweep deletes the record and
removes the bytes. Absent when the attachment follows its
conversation's own retention.
Presigned PUT URL for this one file. Send the raw bytes with no
Authorization header. It cannot read and cannot reach any other
file.
When upload_url stops working, 15 minutes after it was issued.
For a folder member, the path your request sent for it, echoed verbatim. Absent on a standalone reservation.