curl --request POST \
--url https://api.twine.se/v1/org/file-transfers \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"data": {
"content_base64": "JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K",
"file_name": "first-aid-2026.pdf",
"content_type": "application/pdf",
"domain_mapping_id": "018eae56-8f9d-7ca0-bea3-17f3db6cf75b",
"employee_id": "018eae56-8f9d-7ca0-bea3-17f3db6cf75b",
"idempotency_key": "lms-certificate-99213",
"metadata": {
"course_id": "FA-101"
}
},
"job_options": {
"queue_job": true,
"schedule_in": 0
}
}
'import requests
url = "https://api.twine.se/v1/org/file-transfers"
payload = {
"data": {
"content_base64": "JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K",
"file_name": "first-aid-2026.pdf",
"content_type": "application/pdf",
"domain_mapping_id": "018eae56-8f9d-7ca0-bea3-17f3db6cf75b",
"employee_id": "018eae56-8f9d-7ca0-bea3-17f3db6cf75b",
"idempotency_key": "lms-certificate-99213",
"metadata": { "course_id": "FA-101" }
},
"job_options": {
"queue_job": True,
"schedule_in": 0
}
}
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({
data: {
content_base64: 'JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K',
file_name: 'first-aid-2026.pdf',
content_type: 'application/pdf',
domain_mapping_id: '018eae56-8f9d-7ca0-bea3-17f3db6cf75b',
employee_id: '018eae56-8f9d-7ca0-bea3-17f3db6cf75b',
idempotency_key: 'lms-certificate-99213',
metadata: {course_id: 'FA-101'}
},
job_options: {queue_job: true, schedule_in: 0}
})
};
fetch('https://api.twine.se/v1/org/file-transfers', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.twine.se/v1/org/file-transfers",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'data' => [
'content_base64' => 'JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K',
'file_name' => 'first-aid-2026.pdf',
'content_type' => 'application/pdf',
'domain_mapping_id' => '018eae56-8f9d-7ca0-bea3-17f3db6cf75b',
'employee_id' => '018eae56-8f9d-7ca0-bea3-17f3db6cf75b',
'idempotency_key' => 'lms-certificate-99213',
'metadata' => [
'course_id' => 'FA-101'
]
],
'job_options' => [
'queue_job' => true,
'schedule_in' => 0
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.twine.se/v1/org/file-transfers"
payload := strings.NewReader("{\n \"data\": {\n \"content_base64\": \"JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K\",\n \"file_name\": \"first-aid-2026.pdf\",\n \"content_type\": \"application/pdf\",\n \"domain_mapping_id\": \"018eae56-8f9d-7ca0-bea3-17f3db6cf75b\",\n \"employee_id\": \"018eae56-8f9d-7ca0-bea3-17f3db6cf75b\",\n \"idempotency_key\": \"lms-certificate-99213\",\n \"metadata\": {\n \"course_id\": \"FA-101\"\n }\n },\n \"job_options\": {\n \"queue_job\": true,\n \"schedule_in\": 0\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.twine.se/v1/org/file-transfers")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"data\": {\n \"content_base64\": \"JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K\",\n \"file_name\": \"first-aid-2026.pdf\",\n \"content_type\": \"application/pdf\",\n \"domain_mapping_id\": \"018eae56-8f9d-7ca0-bea3-17f3db6cf75b\",\n \"employee_id\": \"018eae56-8f9d-7ca0-bea3-17f3db6cf75b\",\n \"idempotency_key\": \"lms-certificate-99213\",\n \"metadata\": {\n \"course_id\": \"FA-101\"\n }\n },\n \"job_options\": {\n \"queue_job\": true,\n \"schedule_in\": 0\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.twine.se/v1/org/file-transfers")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"data\": {\n \"content_base64\": \"JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K\",\n \"file_name\": \"first-aid-2026.pdf\",\n \"content_type\": \"application/pdf\",\n \"domain_mapping_id\": \"018eae56-8f9d-7ca0-bea3-17f3db6cf75b\",\n \"employee_id\": \"018eae56-8f9d-7ca0-bea3-17f3db6cf75b\",\n \"idempotency_key\": \"lms-certificate-99213\",\n \"metadata\": {\n \"course_id\": \"FA-101\"\n }\n },\n \"job_options\": {\n \"queue_job\": true,\n \"schedule_in\": 0\n }\n}"
response = http.request(request)
puts response.read_body{
"data": {
"file_name": "first-aid-2026.pdf",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"org_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "delivered",
"bytes_retain_until": "2023-11-07T05:31:56Z",
"content_sha256": "<string>",
"content_type": "application/pdf",
"delivered_at": "2023-11-07T05:31:56Z",
"delivery_job_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"domain_mapping_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"employee_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"idempotency_key": "<string>",
"inserted_at": "2023-11-07T05:31:56Z",
"job_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"metadata": {},
"remote_employee_id": "E-10422",
"remote_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"remote_metadata": {},
"remote_path": "/certificates/2026/first-aid-2026.pdf",
"size": 123,
"system_integration_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"updated_at": "2023-11-07T05:31:56Z"
}
}{
"data": {
"file_name": "first-aid-2026.pdf",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"org_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "delivered",
"bytes_retain_until": "2023-11-07T05:31:56Z",
"content_sha256": "<string>",
"content_type": "application/pdf",
"delivered_at": "2023-11-07T05:31:56Z",
"delivery_job_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"domain_mapping_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"employee_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"idempotency_key": "<string>",
"inserted_at": "2023-11-07T05:31:56Z",
"job_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"metadata": {},
"remote_employee_id": "E-10422",
"remote_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"remote_metadata": {},
"remote_path": "/certificates/2026/first-aid-2026.pdf",
"size": 123,
"system_integration_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"updated_at": "2023-11-07T05:31:56Z"
}
}{
"code": 400,
"errors": [],
"message": "Bad Request"
}{
"code": 403,
"errors": [],
"message": "Forbidden"
}{
"code": 422,
"errors": [],
"message": "Unprocessable Entity"
}{
"code": 403,
"errors": [],
"message": "Forbidden"
}Upload File
Upload a file and deliver it to a target system.
The file travels through a file transfer domain mapping that has Twine as its source, so one must be configured. If your organization has exactly one, it is used automatically; otherwise name it with domain_mapping_id — list them with GET /v1/org/domain-mappings and pick the one whose from_domain is file_transfer. If none is configured at all, the upload is still recorded but nothing is sent and status is undeliverable.
Delivery is asynchronous. This call returns as soon as the file is accepted, with status pending and remote_id null. Poll GET /v1/org/file-transfers/{id} until status becomes delivered, then read remote_id (or remote_path) for the identifier the target assigned — that is the value to supply when referencing the document elsewhere, for example as a competence assignment’s document.
Twine does not retain the uploaded file. The record of the transfer, including the target’s identifier, is kept indefinitely; the file itself is removed shortly after delivery.
Supply idempotency_key to make retries safe. Repeating a request with a key already used always returns 200 and the original transfer’s id, never a duplicate record. Whether it also starts a new delivery depends on how the previous attempt ended: delivered, pending and unknown are returned unchanged, while failed, rejected and undeliverable start a fresh attempt on the same record and go back to pending. So a failed document can be retried under the key it already has — a retry re-sends the file posted with it, since Twine does not keep the original bytes.
curl --request POST \
--url https://api.twine.se/v1/org/file-transfers \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"data": {
"content_base64": "JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K",
"file_name": "first-aid-2026.pdf",
"content_type": "application/pdf",
"domain_mapping_id": "018eae56-8f9d-7ca0-bea3-17f3db6cf75b",
"employee_id": "018eae56-8f9d-7ca0-bea3-17f3db6cf75b",
"idempotency_key": "lms-certificate-99213",
"metadata": {
"course_id": "FA-101"
}
},
"job_options": {
"queue_job": true,
"schedule_in": 0
}
}
'import requests
url = "https://api.twine.se/v1/org/file-transfers"
payload = {
"data": {
"content_base64": "JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K",
"file_name": "first-aid-2026.pdf",
"content_type": "application/pdf",
"domain_mapping_id": "018eae56-8f9d-7ca0-bea3-17f3db6cf75b",
"employee_id": "018eae56-8f9d-7ca0-bea3-17f3db6cf75b",
"idempotency_key": "lms-certificate-99213",
"metadata": { "course_id": "FA-101" }
},
"job_options": {
"queue_job": True,
"schedule_in": 0
}
}
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({
data: {
content_base64: 'JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K',
file_name: 'first-aid-2026.pdf',
content_type: 'application/pdf',
domain_mapping_id: '018eae56-8f9d-7ca0-bea3-17f3db6cf75b',
employee_id: '018eae56-8f9d-7ca0-bea3-17f3db6cf75b',
idempotency_key: 'lms-certificate-99213',
metadata: {course_id: 'FA-101'}
},
job_options: {queue_job: true, schedule_in: 0}
})
};
fetch('https://api.twine.se/v1/org/file-transfers', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.twine.se/v1/org/file-transfers",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'data' => [
'content_base64' => 'JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K',
'file_name' => 'first-aid-2026.pdf',
'content_type' => 'application/pdf',
'domain_mapping_id' => '018eae56-8f9d-7ca0-bea3-17f3db6cf75b',
'employee_id' => '018eae56-8f9d-7ca0-bea3-17f3db6cf75b',
'idempotency_key' => 'lms-certificate-99213',
'metadata' => [
'course_id' => 'FA-101'
]
],
'job_options' => [
'queue_job' => true,
'schedule_in' => 0
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.twine.se/v1/org/file-transfers"
payload := strings.NewReader("{\n \"data\": {\n \"content_base64\": \"JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K\",\n \"file_name\": \"first-aid-2026.pdf\",\n \"content_type\": \"application/pdf\",\n \"domain_mapping_id\": \"018eae56-8f9d-7ca0-bea3-17f3db6cf75b\",\n \"employee_id\": \"018eae56-8f9d-7ca0-bea3-17f3db6cf75b\",\n \"idempotency_key\": \"lms-certificate-99213\",\n \"metadata\": {\n \"course_id\": \"FA-101\"\n }\n },\n \"job_options\": {\n \"queue_job\": true,\n \"schedule_in\": 0\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.twine.se/v1/org/file-transfers")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"data\": {\n \"content_base64\": \"JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K\",\n \"file_name\": \"first-aid-2026.pdf\",\n \"content_type\": \"application/pdf\",\n \"domain_mapping_id\": \"018eae56-8f9d-7ca0-bea3-17f3db6cf75b\",\n \"employee_id\": \"018eae56-8f9d-7ca0-bea3-17f3db6cf75b\",\n \"idempotency_key\": \"lms-certificate-99213\",\n \"metadata\": {\n \"course_id\": \"FA-101\"\n }\n },\n \"job_options\": {\n \"queue_job\": true,\n \"schedule_in\": 0\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.twine.se/v1/org/file-transfers")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"data\": {\n \"content_base64\": \"JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9MZW5ndGggNiAwIFI+PgpzdHJlYW0K\",\n \"file_name\": \"first-aid-2026.pdf\",\n \"content_type\": \"application/pdf\",\n \"domain_mapping_id\": \"018eae56-8f9d-7ca0-bea3-17f3db6cf75b\",\n \"employee_id\": \"018eae56-8f9d-7ca0-bea3-17f3db6cf75b\",\n \"idempotency_key\": \"lms-certificate-99213\",\n \"metadata\": {\n \"course_id\": \"FA-101\"\n }\n },\n \"job_options\": {\n \"queue_job\": true,\n \"schedule_in\": 0\n }\n}"
response = http.request(request)
puts response.read_body{
"data": {
"file_name": "first-aid-2026.pdf",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"org_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "delivered",
"bytes_retain_until": "2023-11-07T05:31:56Z",
"content_sha256": "<string>",
"content_type": "application/pdf",
"delivered_at": "2023-11-07T05:31:56Z",
"delivery_job_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"domain_mapping_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"employee_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"idempotency_key": "<string>",
"inserted_at": "2023-11-07T05:31:56Z",
"job_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"metadata": {},
"remote_employee_id": "E-10422",
"remote_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"remote_metadata": {},
"remote_path": "/certificates/2026/first-aid-2026.pdf",
"size": 123,
"system_integration_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"updated_at": "2023-11-07T05:31:56Z"
}
}{
"data": {
"file_name": "first-aid-2026.pdf",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"org_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "delivered",
"bytes_retain_until": "2023-11-07T05:31:56Z",
"content_sha256": "<string>",
"content_type": "application/pdf",
"delivered_at": "2023-11-07T05:31:56Z",
"delivery_job_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"domain_mapping_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"employee_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"idempotency_key": "<string>",
"inserted_at": "2023-11-07T05:31:56Z",
"job_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"metadata": {},
"remote_employee_id": "E-10422",
"remote_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"remote_metadata": {},
"remote_path": "/certificates/2026/first-aid-2026.pdf",
"size": 123,
"system_integration_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"updated_at": "2023-11-07T05:31:56Z"
}
}{
"code": 400,
"errors": [],
"message": "Bad Request"
}{
"code": 403,
"errors": [],
"message": "Forbidden"
}{
"code": 422,
"errors": [],
"message": "Unprocessable Entity"
}{
"code": 403,
"errors": [],
"message": "Forbidden"
}Authorizations
Bearer token for authentication
Body
File Transfer Create Request
Response
Response (an earlier request with this idempotency key already exists)
A file transfer response
A record of one file Twine delivered to a target system. Twine does not keep uploaded files — they are removed shortly after delivery — but this record is kept, including the identifier the target system assigned to the document.
Show child attributes
Show child attributes