curl --request POST \
--url https://api.zivio.net/api/v4/projects/{id}/cancel \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'zivio-tenant-id: <api-key>' \
--data '
{
"reason_code_id": 12,
"custom_reason": "The requirement has been withdrawn by the budget owner.",
"cancellation_reason_id": 3,
"cancellation_other_reason": "Duplicate of an existing engagement."
}
'import requests
url = "https://api.zivio.net/api/v4/projects/{id}/cancel"
payload = {
"reason_code_id": 12,
"custom_reason": "The requirement has been withdrawn by the budget owner.",
"cancellation_reason_id": 3,
"cancellation_other_reason": "Duplicate of an existing engagement."
}
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({
reason_code_id: 12,
custom_reason: 'The requirement has been withdrawn by the budget owner.',
cancellation_reason_id: 3,
cancellation_other_reason: 'Duplicate of an existing engagement.'
})
};
fetch('https://api.zivio.net/api/v4/projects/{id}/cancel', 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}/cancel",
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([
'reason_code_id' => 12,
'custom_reason' => 'The requirement has been withdrawn by the budget owner.',
'cancellation_reason_id' => 3,
'cancellation_other_reason' => 'Duplicate of an existing engagement.'
]),
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}/cancel"
payload := strings.NewReader("{\n \"reason_code_id\": 12,\n \"custom_reason\": \"The requirement has been withdrawn by the budget owner.\",\n \"cancellation_reason_id\": 3,\n \"cancellation_other_reason\": \"Duplicate of an existing engagement.\"\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}/cancel")
.header("Authorization", "Bearer <token>")
.header("zivio-tenant-id", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"reason_code_id\": 12,\n \"custom_reason\": \"The requirement has been withdrawn by the budget owner.\",\n \"cancellation_reason_id\": 3,\n \"cancellation_other_reason\": \"Duplicate of an existing engagement.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.zivio.net/api/v4/projects/{id}/cancel")
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 \"reason_code_id\": 12,\n \"custom_reason\": \"The requirement has been withdrawn by the budget owner.\",\n \"cancellation_reason_id\": 3,\n \"cancellation_other_reason\": \"Duplicate of an existing engagement.\"\n}"
response = http.request(request)
puts response.read_body{
"project_id": 123,
"project_title": "Website Redesign",
"state": "cancelled",
"changed": true,
"message": "Project 123 cancelled",
"eliminated_proposals": 2
}{
"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": "reason_required",
"message": "Please provide a reason for cancellation (reason_code_id is required)",
"reason_codes": [
{
"id": 12,
"label": "Requirement withdrawn"
}
]
}Cancel a project (with a reason)
Cancels the project via the same interactor as the web UI: live milestones are cancelled, open proposals are eliminated (their suppliers are notified), outstanding tasks are cancelled, and the reason is recorded. A reason is required: reason_code_id when the tenant has cancellation reason codes configured, otherwise cancellation_reason_id (optionally with cancellation_other_reason). If the reason is missing or invalid the 422 response lists the valid options — re-submit with one of them. Idempotent: cancelling an already-cancelled project reports changed: false without re-notifying.
curl --request POST \
--url https://api.zivio.net/api/v4/projects/{id}/cancel \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'zivio-tenant-id: <api-key>' \
--data '
{
"reason_code_id": 12,
"custom_reason": "The requirement has been withdrawn by the budget owner.",
"cancellation_reason_id": 3,
"cancellation_other_reason": "Duplicate of an existing engagement."
}
'import requests
url = "https://api.zivio.net/api/v4/projects/{id}/cancel"
payload = {
"reason_code_id": 12,
"custom_reason": "The requirement has been withdrawn by the budget owner.",
"cancellation_reason_id": 3,
"cancellation_other_reason": "Duplicate of an existing engagement."
}
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({
reason_code_id: 12,
custom_reason: 'The requirement has been withdrawn by the budget owner.',
cancellation_reason_id: 3,
cancellation_other_reason: 'Duplicate of an existing engagement.'
})
};
fetch('https://api.zivio.net/api/v4/projects/{id}/cancel', 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}/cancel",
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([
'reason_code_id' => 12,
'custom_reason' => 'The requirement has been withdrawn by the budget owner.',
'cancellation_reason_id' => 3,
'cancellation_other_reason' => 'Duplicate of an existing engagement.'
]),
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}/cancel"
payload := strings.NewReader("{\n \"reason_code_id\": 12,\n \"custom_reason\": \"The requirement has been withdrawn by the budget owner.\",\n \"cancellation_reason_id\": 3,\n \"cancellation_other_reason\": \"Duplicate of an existing engagement.\"\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}/cancel")
.header("Authorization", "Bearer <token>")
.header("zivio-tenant-id", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"reason_code_id\": 12,\n \"custom_reason\": \"The requirement has been withdrawn by the budget owner.\",\n \"cancellation_reason_id\": 3,\n \"cancellation_other_reason\": \"Duplicate of an existing engagement.\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.zivio.net/api/v4/projects/{id}/cancel")
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 \"reason_code_id\": 12,\n \"custom_reason\": \"The requirement has been withdrawn by the budget owner.\",\n \"cancellation_reason_id\": 3,\n \"cancellation_other_reason\": \"Duplicate of an existing engagement.\"\n}"
response = http.request(request)
puts response.read_body{
"project_id": 123,
"project_title": "Website Redesign",
"state": "cancelled",
"changed": true,
"message": "Project 123 cancelled",
"eliminated_proposals": 2
}{
"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": "reason_required",
"message": "Please provide a reason for cancellation (reason_code_id is required)",
"reason_codes": [
{
"id": 12,
"label": "Requirement withdrawn"
}
]
}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
A cancellation reason code id (tenants with reason codes configured). The 422 response lists the valid codes.
12
Free-text detail accompanying the reason code.
"The requirement has been withdrawn by the budget owner."
A cancellation reason id (tenants without reason codes). The 422 response lists the valid reasons.
3
Free-text reason, used with the "other" cancellation reason.
"Duplicate of an existing engagement."
Response
The project state after the call, with how many open proposals were eliminated
The response is of type object.

