Work with files
Files in Inttegro are durable assets with stable file_... IDs. Instead of scattering raw storage URLs across your products and support flows, you upload bytes once, keep the returned file ID, and let Inttegro control access, sharing, and lifecycle. This guide shows you when to upload directly, when to delegate uploads, how to share files safely, and which file-specific failures you should plan for.
File upload, upload-request, and file-link operations are not exposed as MCP tools yet. After file IDs are attached to products, an authenticated AI agent can inspect the product catalog that references them.
MCP tools: list_products or get_product
Prerequisites
- A file purpose chosen before upload:
product_downloadfor downloadable product assets.product_imagefor product images.product_videofor product videos.products_importfor product-catalog CSV imports with the required import schema and no more than 10,000 data rows.support_documentfor support attachments and evidence.
- A plan for where the resulting file ID will live. Product media should store returned file IDs via Create a product or Update a product, not raw third-party URLs.
Choose the right workflow
| Need | Use | Why |
|---|---|---|
| Your backend already has the bytes | Upload a file | Multipart upload from your server. |
| A customer, supplier, or reviewer should send the file themselves | Create an upload request | Inttegro returns a purpose-bound upload URL for one delegated upload. |
| You need revocable public access to a stored file | Create a file link | Inttegro gives you a time-boxed or access-limited public URL. |
Use Download file contents when delivery should stay behind your own server. Stream delivery returns the bytes in the authenticated response; redirect delivery returns a short-lived download URL in the Location header. This is the right delivery path for product_download, products_import, and support_document, because file links only support product_image and product_video.
Send bytes to Inttegro
Choose the upload path that matches who is actually holding the file.
- Server upload
- Delegated upload
Use direct upload when your backend already has the bytes—for example, when an internal admin tool uploads product artwork or when your own service is syncing downloadable assets.
Upload from your server
- cURL
- TypeScript
- Go
- Python
- PHP
- Ruby
- Java
- C#
curl https://api.inttegro.com/files/create \
-H "Authorization: Bearer $INTTEGRO_API_KEY" \
-H "Idempotency-Key: upload-hero-image-001" \
-F "purpose=product_image" \
-F "title=Hero image" \
-F 'custom_data={"product_ref":"SKU-12345"}' \
-F "file=@./hero.png;type=image/png"
import * as Inttegro from '@inttegro/inttegro-sdk'
const inttegro = new Inttegro.InttegroClient({
apiKey: process.env.INTTEGRO_API_KEY!,
})
const result = await inttegro.files.create({
file: "./hero.png",
purpose: "product_image",
title: "Hero image",
customData: {
product_ref: "SKU-12345",
},
}, {
idempotencyKey: "upload-hero-image-001",
})
package main
import (
"context"
"log"
"os"
inttegro "github.com/zebodotdev/inttegro-sdk-go/v4"
)
func main() {
ctx := context.Background()
client := inttegro.NewClient(os.Getenv("INTTEGRO_API_KEY"))
params := inttegro.FileCreateParams{
CustomData: map[string]string{
"product_ref": "SKU-12345",
},
File: "./hero.png",
Purpose: "product_image",
Title: "Hero image",
}
params.IdempotencyKey = "upload-hero-image-001"
result, err := client.Files.Create(ctx, params)
if err != nil {
log.Fatal(err)
}
_ = result
}
import os
import inttegro
client = inttegro.InttegroClient(api_key=os.environ["INTTEGRO_API_KEY"])
result = client.files.create(
file="./hero.png",
purpose="product_image",
title="Hero image",
custom_data={
"product_ref": "SKU-12345",
},
idempotency_key="upload-hero-image-001",
)
<?php
use Inttegro\Client;
$client = new Client($_ENV['INTTEGRO_API_KEY']);
$result = $client->files->create([
'file' => './hero.png',
'purpose' => 'product_image',
'title' => 'Hero image',
'custom_data' => [
'product_ref' => 'SKU-12345',
],
], [
'idempotency_key' => "upload-hero-image-001",
]);
require "inttegro"
client = Inttegro::Client.new(api_key: ENV.fetch("INTTEGRO_API_KEY"))
result = client.files.create(
file: "./hero.png",
purpose: "product_image",
title: "Hero image",
custom_data: {
product_ref: "SKU-12345",
},
idempotency_key: "upload-hero-image-001"
)
import com.inttegro.Client;
import com.inttegro.files.FileCreateParams;
import java.util.Map;
import com.inttegro.RequestOptions;
public class Example {
public static void main(String[] args) throws Exception {
var client = new Client(System.getenv("INTTEGRO_API_KEY"));
var params = FileCreateParams.builder()
.file("./hero.png")
.purpose("product_image")
.title("Hero image")
.customData(Map.<String, String>ofEntries(
Map.entry("product_ref", "SKU-12345")
))
.build();
var result = client.files().create(params, RequestOptions.withIdempotencyKey("upload-hero-image-001"));
}
}
using Inttegro;
using var inttegro = new InttegroClient(
Environment.GetEnvironmentVariable("INTTEGRO_API_KEY")!
);
var result = await inttegro.Files.CreateAsync(new {
file = "./hero.png",
purpose = "product_image",
title = "Hero image",
custom_data = new {
product_ref = "SKU-12345",
},
}, "upload-hero-image-001");
Use custom_data for your own string key-value data. metadata is returned by the API and cannot be supplied.
The response gives you a stable file.id. Store that ID on the Inttegro resource that needs the asset.
Use delegated upload when someone outside your backend should provide the file. Your server creates the request, then passes the returned upload_request.upload_url to the uploader.
Create an upload request
- cURL
- TypeScript
- Go
- Python
- PHP
- Ruby
- Java
- C#
curl https://api.inttegro.com/upload_requests/create \
-H "Authorization: Bearer $INTTEGRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"purpose": "support_document",
"constraints": {
"max_size": 10485760,
"content_types": ["application/pdf"],
"extensions": [".pdf"]
},
"display": {
"title": "Upload proof of purchase",
"help_text": "Attach the PDF receipt from your order confirmation."
},
"recipient": {
"type": "email",
"email": "[email protected]"
},
"resource": {
"type": "case",
"id": "case_123"
},
"expires_at": "2030-06-06T12:30:00Z"
}'
import * as Inttegro from '@inttegro/inttegro-sdk'
const inttegro = new Inttegro.InttegroClient({
apiKey: process.env.INTTEGRO_API_KEY!,
})
const result = await inttegro.uploadRequests.create({
purpose: "support_document",
constraints: {
maxSize: 10485760,
contentTypes: [
"application/pdf",
],
extensions: [
".pdf",
],
},
display: {
title: "Upload proof of purchase",
helpText: "Attach the PDF receipt from your order confirmation.",
},
recipient: {
type: Inttegro.ChimeRecipientTypes.Email,
},
resource: {
type: "case",
id: "case_123",
},
expiresAt: "2030-06-06T12:30:00Z",
})
package main
import (
"context"
"log"
"os"
inttegro "github.com/zebodotdev/inttegro-sdk-go/v4"
)
func main() {
ctx := context.Background()
client := inttegro.NewClient(os.Getenv("INTTEGRO_API_KEY"))
params := inttegro.UploadRequestCreateParams{
Purpose: "support_document",
Constraints: inttegro.UploadRequestConstraints{
MaxSize: 10485760,
ContentTypes: []string{
"application/pdf",
},
Extensions: []string{
".pdf",
},
},
Display: inttegro.UploadRequestDisplay{
Title: "Upload proof of purchase",
HelpText: "Attach the PDF receipt from your order confirmation.",
},
Recipient: inttegro.FileParty{
Type: "email",
},
Resource: inttegro.FileResource{
Type: "case",
ID: "case_123",
},
ExpiresAt: "2030-06-06T12:30:00Z",
}
result, err := client.UploadRequests.Create(ctx, params)
if err != nil {
log.Fatal(err)
}
_ = result
}
import os
import inttegro
client = inttegro.InttegroClient(api_key=os.environ["INTTEGRO_API_KEY"])
result = client.upload_requests.create(inttegro.upload_requests.CreateRequest(
purpose="support_document",
constraints=inttegro.upload_requests.Constraints(
max_size=10485760,
content_types=[
"application/pdf",
],
extensions=[
".pdf",
],
),
display=inttegro.upload_requests.Display(
title="Upload proof of purchase",
help_text="Attach the PDF receipt from your order confirmation.",
),
recipient=inttegro.FilePartyInput(
type=inttegro.ChimeRecipientType.EMAIL,
),
resource=inttegro.FileResourceInput(
type="case",
id="case_123",
),
expires_at="2030-06-06T12:30:00Z",
))
<?php
use Inttegro\Client;
$client = new Client($_ENV['INTTEGRO_API_KEY']);
$result = $client->uploadRequests->create([
'purpose' => 'support_document',
'constraints' => [
'max_size' => 10485760,
'content_types' => [
'application/pdf',
],
'extensions' => [
'.pdf',
],
],
'display' => [
'title' => 'Upload proof of purchase',
'help_text' => 'Attach the PDF receipt from your order confirmation.',
],
'recipient' => [
'type' => \Inttegro\ChimeRecipientType::Email,
],
'resource' => [
'type' => 'case',
'id' => 'case_123',
],
'expires_at' => '2030-06-06T12:30:00Z',
]);
require "inttegro"
client = Inttegro::Client.new(api_key: ENV.fetch("INTTEGRO_API_KEY"))
result = client.upload_requests.create(
purpose: "support_document",
constraints: {
max_size: 10485760,
content_types: [
"application/pdf",
],
extensions: [
".pdf",
],
},
display: {
title: "Upload proof of purchase",
help_text: "Attach the PDF receipt from your order confirmation.",
},
recipient: {
type: Inttegro::ChimeRecipientType::EMAIL,
},
resource: {
type: "case",
id: "case_123",
},
expires_at: "2030-06-06T12:30:00Z"
)
import com.inttegro.Client;
import com.inttegro.files.UploadRequestCreateParams;
import com.inttegro.files.UploadConstraints;
import java.util.List;
import com.inttegro.files.UploadDisplay;
import com.inttegro.files.Actor;
import com.inttegro.files.ResourceRef;
public class Example {
public static void main(String[] args) throws Exception {
var client = new Client(System.getenv("INTTEGRO_API_KEY"));
var params = UploadRequestCreateParams.builder()
.purpose("support_document")
.constraints(UploadConstraints.builder()
.maxSize(10485760)
.contentTypes(List.of(
"application/pdf"
))
.extensions(List.of(
".pdf"
))
.build())
.display(UploadDisplay.builder()
.title("Upload proof of purchase")
.helpText("Attach the PDF receipt from your order confirmation.")
.build())
.recipient(Actor.builder()
.type("email")
.build())
.resource(ResourceRef.builder()
.type("case")
.id("case_123")
.build())
.expiresAt("2030-06-06T12:30:00Z")
.build();
var result = client.uploadRequests().create(params, null);
}
}
using Inttegro;
using var inttegro = new InttegroClient(
Environment.GetEnvironmentVariable("INTTEGRO_API_KEY")!
);
var result = await inttegro.UploadRequests.CreateAsync(new {
purpose = "support_document",
constraints = new {
max_size = 10485760,
content_types = new[] {
"application/pdf",
},
extensions = new[] {
".pdf",
},
},
display = new {
title = "Upload proof of purchase",
help_text = "Attach the PDF receipt from your order confirmation.",
},
recipient = new {
type = Inttegro.ChimeRecipientType.Email,
},
resource = new {
type = "case",
id = "case_123",
},
expires_at = "2030-06-06T12:30:00Z",
});
The request expires after 24 hours unless you set a future expires_at, and permits three failed attempts unless you set attempts.max_attempts. Size values must be nonnegative and remain within the selected purpose; MIME types, extensions, and an exact filename can only narrow that purpose. See Create an upload request for the complete validation rules.
Store the returned upload_request.upload_url securely and give it only to the uploader. It is returned once by create and is not returned by lookup, page, or cancel. Have the uploader send multipart/form-data directly to Fulfill an upload request.
Share files safely
File links are for controlled public access. They work for product_image and product_video, and they let you set delivery behavior, expiry, and access limits.
The inline example keeps allow_download: true because attachment disposition is valid only when downloads are allowed. File links expire after 24 hours by default, and max_accesses: 0 means unlimited successful opens.
Create a public file link
- cURL
- TypeScript
- Go
- Python
- PHP
- Ruby
- Java
- C#
curl https://api.inttegro.com/file_links/create \
-H "Authorization: Bearer $INTTEGRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"file_id": "file_4q6YcQk1RzPv2mDa8nFw0sHu",
"delivery": {
"mode": "inline"
},
"access": {
"allow_download": true,
"max_accesses": 25
},
"expires_at": "2030-06-06T12:30:00Z"
}'
import * as Inttegro from '@inttegro/inttegro-sdk'
const inttegro = new Inttegro.InttegroClient({
apiKey: process.env.INTTEGRO_API_KEY!,
})
const result = await inttegro.fileLinks.create({
fileId: "file_4q6YcQk1RzPv2mDa8nFw0sHu",
delivery: {
mode: "inline",
},
access: {
allowDownload: true,
maxAccesses: 25,
},
expiresAt: "2030-06-06T12:30:00Z",
})
package main
import (
"context"
"log"
"os"
inttegro "github.com/zebodotdev/inttegro-sdk-go/v4"
)
func main() {
ctx := context.Background()
client := inttegro.NewClient(os.Getenv("INTTEGRO_API_KEY"))
params := inttegro.FileLinkCreateParams{
FileID: "file_4q6YcQk1RzPv2mDa8nFw0sHu",
Delivery: inttegro.FileLinkDelivery{
Mode: "inline",
},
Access: inttegro.FileLinkAccess{
AllowDownload: true,
MaxAccesses: 25,
},
ExpiresAt: "2030-06-06T12:30:00Z",
}
fileLink, url, err := client.FileLinks.Create(ctx, params)
if err != nil {
log.Fatal(err)
}
_ = fileLink
_ = url
}
import os
import inttegro
client = inttegro.InttegroClient(api_key=os.environ["INTTEGRO_API_KEY"])
result = client.file_links.create(inttegro.file_links.CreateRequest(
file_id="file_4q6YcQk1RzPv2mDa8nFw0sHu",
delivery=inttegro.file_links.Delivery(
mode="inline",
),
access=inttegro.file_links.Access(
allow_download=True,
max_accesses=25,
),
expires_at="2030-06-06T12:30:00Z",
))
<?php
use Inttegro\Client;
$client = new Client($_ENV['INTTEGRO_API_KEY']);
$result = $client->fileLinks->create([
'file_id' => 'file_4q6YcQk1RzPv2mDa8nFw0sHu',
'delivery' => [
'mode' => 'inline',
],
'access' => [
'allow_download' => true,
'max_accesses' => 25,
],
'expires_at' => '2030-06-06T12:30:00Z',
]);
require "inttegro"
client = Inttegro::Client.new(api_key: ENV.fetch("INTTEGRO_API_KEY"))
result = client.file_links.create(
file_id: "file_4q6YcQk1RzPv2mDa8nFw0sHu",
delivery: {
mode: "inline",
},
access: {
allow_download: true,
max_accesses: 25,
},
expires_at: "2030-06-06T12:30:00Z"
)
import com.inttegro.Client;
import com.inttegro.files.FileLinkCreateParams;
import com.inttegro.files.FileLinkDelivery;
import com.inttegro.files.FileLinkDeliveryMode;
import com.inttegro.files.FileLinkAccess;
public class Example {
public static void main(String[] args) throws Exception {
var client = new Client(System.getenv("INTTEGRO_API_KEY"));
var params = FileLinkCreateParams.builder()
.fileId("file_4q6YcQk1RzPv2mDa8nFw0sHu")
.delivery(FileLinkDelivery.builder()
.mode(FileLinkDeliveryMode.INLINE)
.build())
.access(FileLinkAccess.builder()
.allowDownload(true)
.maxAccesses(25)
.build())
.expiresAt("2030-06-06T12:30:00Z")
.build();
var result = client.fileLinks().create(params, null);
}
}
using Inttegro;
using var inttegro = new InttegroClient(
Environment.GetEnvironmentVariable("INTTEGRO_API_KEY")!
);
var result = await inttegro.FileLinks.CreateAsync(new {
file_id = "file_4q6YcQk1RzPv2mDa8nFw0sHu",
delivery = new {
mode = "inline",
},
access = new {
allow_download = true,
max_accesses = 25,
},
expires_at = "2030-06-06T12:30:00Z",
});
Give the returned top-level url to the consumer. They open that URL via Open a file link. If access should end early, revoke it with Revoke a file link.
Attach file IDs to products
Treat Inttegro file IDs as the contract between your product catalog and stored media.
- Use
product_imagefor hero images and galleries. - Use
product_videofor product videos you may want to share publicly. - Use
product_downloadfor downloadable assets delivered after purchase. - Use
products_importonly for CSV product-catalog imports. The file must follow the required import schema, contain no more than 10,000 data rows, and stay within 32 MiB.
Store the returned file IDs on your product records through Create a product or Update a product. Do not save temporary upload URLs, local filenames, or third-party CDN URLs as your source of truth. That breaks lifecycle controls, makes deletions harder, and bypasses the purpose validation you applied at upload time.
Handle file-specific failures
File APIs return stable error codes. Use Error Codes to decide whether to ask for a different file, retry later, refresh an expired public URL, or clean up the resource that still references the file.
Related resources
- Upload a file - Multipart upload from your own backend.
- Download file contents - Server-side delivery for any stored file.
- Create a file link - Revocable public access for linkable files.
- Create an upload request - Delegated upload for customers or external partners.
- Update a product - Store returned file IDs on product records.