curl --request POST \
--url https://api.zivio.net/api/v4/projects/{id}/submit \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'zivio-tenant-id: <api-key>' \
--data '
{
"approvers": {
"12": [
34,
56
]
}
}
'import requests
url = "https://api.zivio.net/api/v4/projects/{id}/submit"
payload = { "approvers": { "12": [34, 56] } }
headers = {
"Authorization": "Bearer <token>",
"zivio-tenant-id": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
Authorization: 'Bearer <token>',
'zivio-tenant-id': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({approvers: {'12': [34, 56]}})
};
fetch('https://api.zivio.net/api/v4/projects/{id}/submit', 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.zivio.net/api/v4/projects/{id}/submit",
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([
'approvers' => [
'12' => [
34,
56
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"zivio-tenant-id: <api-key>"
],
]);
$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.zivio.net/api/v4/projects/{id}/submit"
payload := strings.NewReader("{\n \"approvers\": {\n \"12\": [\n 34,\n 56\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("zivio-tenant-id", "<api-key>")
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.zivio.net/api/v4/projects/{id}/submit")
.header("Authorization", "Bearer <token>")
.header("zivio-tenant-id", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"approvers\": {\n \"12\": [\n 34,\n 56\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.zivio.net/api/v4/projects/{id}/submit")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["zivio-tenant-id"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"approvers\": {\n \"12\": [\n 34,\n 56\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"state": "pending",
"title": "Website Redesign"
}{
"error": "invalid_token",
"error_description": "The access token provided is expired, revoked, malformed, or invalid for other reasons"
}{
"error": "insufficient_scope",
"error_description": "The request requires higher privileges than provided by the access token",
"required_scope": "projects:write",
"provided_scopes": [
"welcome:read"
]
}{
"error": "approver_selection_required",
"approver_selection": [
{
"step_id": 12,
"name": "Finance",
"level": 0,
"approvers": [
{
"id": 34,
"name": "Jane Smith"
}
]
}
]
}Submit a draft project for approval (publish)
Submits a draft project into the approval workflow, mirroring the web UI publish action. The project must pass publish validations. Depending on tenant configuration the project either auto-approves (state new), or moves to pending while approvers action it. If the organisation’s approval steps require approver selection, the call returns 422 with error: approver_selection_required and an approver_selection array listing each step’s eligible approvers — re-submit with the approvers object populated.
curl --request POST \
--url https://api.zivio.net/api/v4/projects/{id}/submit \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'zivio-tenant-id: <api-key>' \
--data '
{
"approvers": {
"12": [
34,
56
]
}
}
'import requests
url = "https://api.zivio.net/api/v4/projects/{id}/submit"
payload = { "approvers": { "12": [34, 56] } }
headers = {
"Authorization": "Bearer <token>",
"zivio-tenant-id": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
Authorization: 'Bearer <token>',
'zivio-tenant-id': '<api-key>',
'Content-Type': 'application/json'
},
body: JSON.stringify({approvers: {'12': [34, 56]}})
};
fetch('https://api.zivio.net/api/v4/projects/{id}/submit', 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.zivio.net/api/v4/projects/{id}/submit",
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([
'approvers' => [
'12' => [
34,
56
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"zivio-tenant-id: <api-key>"
],
]);
$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.zivio.net/api/v4/projects/{id}/submit"
payload := strings.NewReader("{\n \"approvers\": {\n \"12\": [\n 34,\n 56\n ]\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("zivio-tenant-id", "<api-key>")
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.zivio.net/api/v4/projects/{id}/submit")
.header("Authorization", "Bearer <token>")
.header("zivio-tenant-id", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"approvers\": {\n \"12\": [\n 34,\n 56\n ]\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.zivio.net/api/v4/projects/{id}/submit")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["zivio-tenant-id"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"approvers\": {\n \"12\": [\n 34,\n 56\n ]\n }\n}"
response = http.request(request)
puts response.read_body{
"id": 123,
"state": "pending",
"title": "Website Redesign"
}{
"error": "invalid_token",
"error_description": "The access token provided is expired, revoked, malformed, or invalid for other reasons"
}{
"error": "insufficient_scope",
"error_description": "The request requires higher privileges than provided by the access token",
"required_scope": "projects:write",
"provided_scopes": [
"welcome:read"
]
}{
"error": "approver_selection_required",
"approver_selection": [
{
"step_id": 12,
"name": "Finance",
"level": 0,
"approvers": [
{
"id": 34,
"name": "Jane Smith"
}
]
}
]
}Authorizations
OAuth 2.0 client credentials. The access token from POST /oauth/token is sent as a bearer token. Request only the scopes you need.
Your Zivio organisation identifier. The regional API endpoints serve every Zivio organisation, so each request must identify yours.
Path Parameters
Project ID
Body
Approver selection keyed by approval step id, each value an array of approver user ids. Only needed when a prior call returned approver_selection_required.
{ "12": [34, 56] }
Response
Project submitted; response is the serialized project (state new or pending)
The response is of type object.

