Decline Invite
curl --request POST \
--url https://api.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"actingUserId": "<string>"
}
'import requests
url = "https://api.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline"
payload = { "actingUserId": "<string>" }
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({actingUserId: '<string>'})
};
fetch('https://api.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline', 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.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline",
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([
'actingUserId' => '<string>'
]),
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.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline"
payload := strings.NewReader("{\n \"actingUserId\": \"<string>\"\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.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"actingUserId\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline")
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 \"actingUserId\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyWorkspace — Invitations
Decline Invite
Decline an invitation — identity-matched and verification-gated
Decline Invite
curl --request POST \
--url https://api.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"actingUserId": "<string>"
}
'import requests
url = "https://api.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline"
payload = { "actingUserId": "<string>" }
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({actingUserId: '<string>'})
};
fetch('https://api.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline', 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.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline",
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([
'actingUserId' => '<string>'
]),
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.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline"
payload := strings.NewReader("{\n \"actingUserId\": \"<string>\"\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.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"actingUserId\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.sublay.io/v7/:projectId/workspace-invites/:inviteId/decline")
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 \"actingUserId\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyDeclines an invitation. Identity-matched (the caller must be the invite’s target) and verification-gated — a verified email is required, exactly as it is to accept. Without that gate, someone who signed up with another person’s address and never verified it could burn the real target’s invite by moving it to the terminal
The
A service/master key called this route without naming an
A plain user token sent an
Every path id on the workspaces bundle is checked for UUID shape before the route runs, so a malformed one is a plain
See also: useDeclineWorkspaceInvite
declined state. This is not a key-bypass gate: a service or master key acting as a named actingUserId still requires that user to be verified.
Marks the invite declined and fires workspace.invite.declined.
Path Parameters
string
required
The invitation UUID.
Body Parameters
string
required
Service/master keys only — the declining user (must be the invite’s target). Required for a key: a decline is an act by the invitee, so there is no unbounded path — a key that names nobody is refused with
400 workspace/missing-user-id.A plain user token omits it — the actor is the token’s own user. The field is not ignored for such a token: sending your own user id is a harmless no-op, but sending anyone else’s is a 403 workspace/unauthorized. See Acting on behalf of a user.Response
{ "success": true }
Error Responses
Email Not Verified — 403
Email Not Verified — 403
{ "error": "A verified email is required to decline an invitation.", "code": "workspace/email-not-verified" }
Not the Target — 404
Not the Target — 404
A caller who is not the invitation’s target receives the same
404 as a
non-existent invitation.{ "error": "Invitation not found.", "code": "workspace/invite-not-found" }
Invite Terminal — 409
Invite Terminal — 409
{ "error": "This invitation is no longer pending.", "code": "workspace/invite-terminal" }
Acting User Not Found — 404
Acting User Not Found — 404
{ "error": "Acting user not found.", "code": "workspace/invalid-target" }
actingUserId a key named does not exist in this project. Naming is not authentication — the id is only looked up here.Missing Acting User — 400
Missing Acting User — 400
{ "error": "Missing user ID", "code": "workspace/missing-user-id" }
actingUserId. A decline is an act by the invitee; there is nobody to attribute it to.Acted as Another User — 403
Acted as Another User — 403
{ "error": "Unauthorized", "code": "workspace/unauthorized" }
actingUserId naming a different user. Only a service/master key may act as someone else.Invalid Path Parameter — 400
Invalid Path Parameter — 400
{ "error": "Invalid invitation id: expected a UUID.", "code": "workspace/invalid-params" }
400 rather than a 500 from the database.
