Contents
Uploads
Brain only. How an AI client attaches a file it produced to a deal:
- Call the tool
create_file_uploadwith the file name (PDF, PPTX, DOCX, XLSX, PNG or JPG, up to 25 MB). It returnsupload_id, a signedupload_url, theheadersto send andexpires_at. - Send the file to
upload_urlwith an HTTPPUT, the raw bytes as body (below). The URL works once and for 15 minutes; quote it in your shell, it carriesexpiresandsignature. - Call the tool
attach_file_to_dealwithupload_idanddeal_id: the file becomes a draft document of the deal (or a new version of an uploaded document, or the official PDF of an HTML document), which the person approves and sends from Brain.
An upload that is never attached is removed after 24 hours.
Base URL, authentication and conventions of this API are on the AI Tools API page.
/api/v1/public/uploads/{upload}Upload a file to a signed URL
Second step of a file upload: send the file to the upload_url returned by the Brain tool
create_file_upload, unchanged. No bearer token: the signature in the URL is the credential, and
the upload already belongs to the person and the company that asked for it.
- Body: the raw bytes of the file (not multipart, not base64), up to 25 MB.
- Content-Type: the value returned in
headers(for exampleapplication/pdf). With curl use--data-binary @file, otherwise curl sendsapplication/x-www-form-urlencoded. - Checks: Nucleo reads the first bytes of the file and refuses one whose content does not
match the extension given to
create_file_upload(a PDF must start with%PDF-, an Office file must be a real DOCX, PPTX or XLSX). - Single use: the URL accepts one
PUTwithin 15 minutes. After a refusal, start again withcreate_file_upload.
Then call the tool attach_file_to_deal with the upload_id. Error messages are currently in
Italian; send Accept: application/json to get JSON errors from the signature check too.
Path parameters
uploadstring (uuid)requiredThe
upload_idreturned bycreate_file_upload.Example:9b2f4c1e-5d7a-4e8b-9c3d-2a1f0e6b7c8d
Query parameters
expiresintegerrequiredPart of the signed URL (Unix time of expiry). Do not change it.
Example:1791880200signaturestringrequiredPart of the signed URL. Do not change it.
Example:4f6b0d1c2e3a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9a0b1c2d3e
Request bodyapplication/pdf, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, image/png, image/jpeg
The file, as raw bytes.
string (binary)
Responses
200File received and checked. Attach it with
attach_file_to_deal.application/jsonupload_idstring (uuid)status"completed"size_bytesintegercontent_typestringnextstringWhat to do next
403Invalid or expired signature (the URL was changed, or more than 15 minutes have passed).application/json
messagestring
404Unknown upload (for example one removed after 24 hours).application/json
messagestring
409The file was refused and the URL cannot be used again: already used, expired, empty, larger than 25 MB, or content that does not match the extension.
statusis the state of the upload. application/jsonmessagestringThe reason (currently in Italian).
statusstring | nullState of the upload after the refusal.
One ofpendingreceivingcompletedattachedfailednull
413Body larger than 25 MB, refused before it reaches Brain.
429More than 30 requests per minute from the same IP address.application/json
HeadersRetry-After
messagestring
curl -X PUT 'https://api-brain.nucleoplatform.com/api/v1/public/uploads/9b2f4c1e-5d7a-4e8b-9c3d-2a1f0e6b7c8d?expires=1791880200&signature=4f6b0d1c2e3a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9a0b1c2d3e' \
-H 'Content-Type: application/pdf' \
-H 'Accept: application/json' \
--data-binary @quote.pdfimport { readFile } from "node:fs/promises";
const body = await readFile("quote.pdf");
const res = await fetch("https://api-brain.nucleoplatform.com/api/v1/public/uploads/9b2f4c1e-5d7a-4e8b-9c3d-2a1f0e6b7c8d?expires=1791880200&signature=4f6b0d1c2e3a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9a0b1c2d3e", {
method: "PUT",
headers: {
"Content-Type": "application/pdf",
Accept: "application/json",
},
body,
});
const data = await res.json();
console.log(res.status, data);<?php
require 'vendor/autoload.php';
$client = new GuzzleHttp\Client();
$response = $client->request('PUT', 'https://api-brain.nucleoplatform.com/api/v1/public/uploads/9b2f4c1e-5d7a-4e8b-9c3d-2a1f0e6b7c8d?expires=1791880200&signature=4f6b0d1c2e3a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f9a0b1c2d3e', [
'headers' => [
'Content-Type' => 'application/pdf',
'Accept' => 'application/json',
],
'body' => fopen('quote.pdf', 'rb'),
'http_errors' => false,
]);
echo $response->getStatusCode(), PHP_EOL;
echo $response->getBody();{
"upload_id": "9b2f4c1e-5d7a-4e8b-9c3d-2a1f0e6b7c8d",
"status": "completed",
"size_bytes": 482113,
"content_type": "application/pdf",
"next": "File ricevuto: ora chiama attach_file_to_deal con questo upload_id."
}{
"message": "Invalid signature."
}{
"message": "Upload non trovato."
}{
"message": "Questo URL di caricamento è già stato usato.",
"status": "completed"
}{
"message": "Il contenuto del file non corrisponde al tipo .pdf.",
"status": "failed"
}{
"message": "Too Many Attempts."
}