curl --request PATCH \
--url https://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'authorization: <authorization>' \
--data '
{
"upsert": [],
"remove": []
}
'import requests
url = "https://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields"
payload = {
"upsert": [],
"remove": []
}
headers = {
"authorization": "<authorization>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {
authorization: '<authorization>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({upsert: [], remove: []})
};
fetch('https://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields', 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://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'upsert' => [
],
'remove' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"authorization: <authorization>"
],
]);
$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://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields"
payload := strings.NewReader("{\n \"upsert\": [],\n \"remove\": []\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("authorization", "<authorization>")
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.patch("https://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields")
.header("authorization", "<authorization>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"upsert\": [],\n \"remove\": []\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["authorization"] = '<authorization>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"upsert\": [],\n \"remove\": []\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"type": "TEXT",
"position": 123,
"fieldMeta": {
"type": "TEXT",
"content": "<string>",
"locked": true,
"visibilityRule": {
"customInputId": "<string>",
"equals": "<string>",
"mode": "SHOW_WHEN"
},
"hidden": true
},
"documentId": "<string>"
}{
"message": "<string>",
"code": "<string>",
"errorId": "<string>",
"issues": [
{
"path": "<string>",
"message": "<string>"
}
],
"requiredScopes": [
"<string>"
],
"grantedScopes": [
"<string>"
]
}{
"message": "<string>",
"code": "<string>",
"errorId": "<string>",
"issues": [
{
"path": "<string>",
"message": "<string>"
}
],
"requiredScopes": [
"<string>"
],
"grantedScopes": [
"<string>"
]
}{
"message": "<string>",
"code": "<string>",
"errorId": "<string>",
"issues": [
{
"path": "<string>",
"message": "<string>"
}
],
"requiredScopes": [
"<string>"
],
"grantedScopes": [
"<string>"
]
}Add, update, or remove boxes on a PDF field
Changes individual boxes on a PDF field without sending the whole field. Only DRAFT documents can be changed.
Each box in upsert has the same shape as an entry in fieldMeta.placedFields on POST /api/v1/documents/{id}/fields, and is checked the same way. A box whose id already exists on the field replaces that box and keeps its stacking order. A box with a new id is added on top. remove lists the ids of boxes to delete. Boxes you don’t mention stay as they are.
The change is atomic: if any box is invalid, nothing is written and the response lists every problem in issues. If another edit changes the field while the request runs, the request fails with an HTTP 400 Bad Request status code and code INVALID_STATE; send it again.
Example - move one box and remove another:
PATCH /api/v1/documents/{docId}/fields/{fieldId}/placed-fields
{
"upsert": [
{
"id": "sig-1",
"kind": "input",
"inputType": "signature",
"page": 2,
"rect": { "x": 72, "y": 120, "width": 200, "height": 48 },
"signerId": "PARTY_ID"
}
],
"remove": ["initials-3"]
}
curl --request PATCH \
--url https://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'authorization: <authorization>' \
--data '
{
"upsert": [],
"remove": []
}
'import requests
url = "https://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields"
payload = {
"upsert": [],
"remove": []
}
headers = {
"authorization": "<authorization>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {
authorization: '<authorization>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({upsert: [], remove: []})
};
fetch('https://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields', 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://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'upsert' => [
],
'remove' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"authorization: <authorization>"
],
]);
$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://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields"
payload := strings.NewReader("{\n \"upsert\": [],\n \"remove\": []\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("authorization", "<authorization>")
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.patch("https://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields")
.header("authorization", "<authorization>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"upsert\": [],\n \"remove\": []\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.sajn.se/api/v1/documents/{id}/fields/{fieldId}/placed-fields")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["authorization"] = '<authorization>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"upsert\": [],\n \"remove\": []\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"type": "TEXT",
"position": 123,
"fieldMeta": {
"type": "TEXT",
"content": "<string>",
"locked": true,
"visibilityRule": {
"customInputId": "<string>",
"equals": "<string>",
"mode": "SHOW_WHEN"
},
"hidden": true
},
"documentId": "<string>"
}{
"message": "<string>",
"code": "<string>",
"errorId": "<string>",
"issues": [
{
"path": "<string>",
"message": "<string>"
}
],
"requiredScopes": [
"<string>"
],
"grantedScopes": [
"<string>"
]
}{
"message": "<string>",
"code": "<string>",
"errorId": "<string>",
"issues": [
{
"path": "<string>",
"message": "<string>"
}
],
"requiredScopes": [
"<string>"
],
"grantedScopes": [
"<string>"
]
}{
"message": "<string>",
"code": "<string>",
"errorId": "<string>",
"issues": [
{
"path": "<string>",
"message": "<string>"
}
],
"requiredScopes": [
"<string>"
],
"grantedScopes": [
"<string>"
]
}Authorizations
Personal API key, e.g. Authorization: Bearer sajn_sk_.... Not scope-limited — acts as the issuing user.
Headers
Bearer token for API authentication
Makes retries safe. A retry with the same key and the same request replays the stored response for 24 hours (header Idempotent-Replayed: true); the same key with a different request returns 400.
255Body
Body
Boxes to add or replace. A box whose id already exists on the field replaces that box and keeps its stacking order; any other box is added on top.
500A box placed on a page of a PDF field
- Option 1
- Option 2
Show child attributes
Show child attributes
Ids of boxes to remove from the field.
5001Response
200
The PDF field id
TEXT, HTML, FORM, PDF, PRODUCT_TABLE, TABLE, SPACER, PAGE_BREAK, DURATION The PDF field metadata after the change, including every box in placedFields
- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
- Option 6
- Option 7
- Option 8
- Option 9
Show child attributes
Show child attributes
Was this page helpful?

