Workspace
Resume workspace setup after payment verification
Consumes the short-lived return token issued with a setup intent and rotates the session resume credential atomically. It is bound to the public session, current setup intent and current resume credential, so it cannot be replayed after rotation. Token lifetime is at most15 minutes and never exceeds session expiry.
POST
/
workspace-onboarding
/
payment-return
/
exchange
Resume workspace setup after payment verification
curl --request POST \
--url https://api.gratona.com/v1/workspace-onboarding/payment-return/exchange \
--header 'Content-Type: application/json' \
--data '
{
"public_id": "550e8400-e29b-41d4-a716-446655440000",
"return_token": "1760961600.0000000000000000000000000000000000000000000000000000000000000000"
}
'import requests
url = "https://api.gratona.com/v1/workspace-onboarding/payment-return/exchange"
payload = {
"public_id": "550e8400-e29b-41d4-a716-446655440000",
"return_token": "1760961600.0000000000000000000000000000000000000000000000000000000000000000"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
public_id: '550e8400-e29b-41d4-a716-446655440000',
return_token: '1760961600.0000000000000000000000000000000000000000000000000000000000000000'
})
};
fetch('https://api.gratona.com/v1/workspace-onboarding/payment-return/exchange', 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.gratona.com/v1/workspace-onboarding/payment-return/exchange",
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([
'public_id' => '550e8400-e29b-41d4-a716-446655440000',
'return_token' => '1760961600.0000000000000000000000000000000000000000000000000000000000000000'
]),
CURLOPT_HTTPHEADER => [
"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.gratona.com/v1/workspace-onboarding/payment-return/exchange"
payload := strings.NewReader("{\n \"public_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"return_token\": \"1760961600.0000000000000000000000000000000000000000000000000000000000000000\"\n}")
req, _ := http.NewRequest("POST", url, payload)
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.gratona.com/v1/workspace-onboarding/payment-return/exchange")
.header("Content-Type", "application/json")
.body("{\n \"public_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"return_token\": \"1760961600.0000000000000000000000000000000000000000000000000000000000000000\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/workspace-onboarding/payment-return/exchange")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"public_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"return_token\": \"1760961600.0000000000000000000000000000000000000000000000000000000000000000\"\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"public_id": "550e8400-e29b-41d4-a716-446655440000",
"state": "authenticated",
"brand_id": "gratona",
"email": "jane@example.org",
"answers": {
"primary_goal": "fundraising_campaigns",
"starting_point": "launching_new"
},
"automatic_billing": false,
"qualification": "verify_payment_method",
"expires_at": "2026-10-21T12:00:00Z",
"first_name": "Jane",
"last_name": "Donor",
"organization_name": "Example Nonprofit",
"website": "https://example.org/",
"ein": "123456789",
"country_code": "US",
"phone": "+12025550123",
"questions": [
{
"key": "primary_goal",
"prompt": "What would you like Gratona to help you set up first?",
"type": "single_choice",
"required": true,
"options": [
{
"value": "fundraising_campaigns",
"label": "Fundraising campaigns and donations"
}
]
}
]
},
"session_token": "example-session-token-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}{
"status": "error",
"code": "workspace_onboarding_session_unavailable",
"message": "This setup session is no longer available. Start again."
}{
"status": "error",
"code": "workspace_onboarding_session_unavailable",
"message": "This setup session is no longer available. Start again."
}{
"status": "error",
"code": "workspace_onboarding_session_unavailable",
"message": "This setup session is no longer available. Start again."
}Body
application/json
Validated request fields for this operation.
Public setup-session UUID.
Example:
"550e8400-e29b-41d4-a716-446655440000"
Signed return token from payment_return.token.
Maximum string length:
96Pattern:
^\d{10}\.[a-f0-9]{64}$Example:
"1760961600.0000000000000000000000000000000000000000000000000000000000000000"
⌘I
Resume workspace setup after payment verification
curl --request POST \
--url https://api.gratona.com/v1/workspace-onboarding/payment-return/exchange \
--header 'Content-Type: application/json' \
--data '
{
"public_id": "550e8400-e29b-41d4-a716-446655440000",
"return_token": "1760961600.0000000000000000000000000000000000000000000000000000000000000000"
}
'import requests
url = "https://api.gratona.com/v1/workspace-onboarding/payment-return/exchange"
payload = {
"public_id": "550e8400-e29b-41d4-a716-446655440000",
"return_token": "1760961600.0000000000000000000000000000000000000000000000000000000000000000"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
public_id: '550e8400-e29b-41d4-a716-446655440000',
return_token: '1760961600.0000000000000000000000000000000000000000000000000000000000000000'
})
};
fetch('https://api.gratona.com/v1/workspace-onboarding/payment-return/exchange', 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.gratona.com/v1/workspace-onboarding/payment-return/exchange",
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([
'public_id' => '550e8400-e29b-41d4-a716-446655440000',
'return_token' => '1760961600.0000000000000000000000000000000000000000000000000000000000000000'
]),
CURLOPT_HTTPHEADER => [
"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.gratona.com/v1/workspace-onboarding/payment-return/exchange"
payload := strings.NewReader("{\n \"public_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"return_token\": \"1760961600.0000000000000000000000000000000000000000000000000000000000000000\"\n}")
req, _ := http.NewRequest("POST", url, payload)
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.gratona.com/v1/workspace-onboarding/payment-return/exchange")
.header("Content-Type", "application/json")
.body("{\n \"public_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"return_token\": \"1760961600.0000000000000000000000000000000000000000000000000000000000000000\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.gratona.com/v1/workspace-onboarding/payment-return/exchange")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"public_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"return_token\": \"1760961600.0000000000000000000000000000000000000000000000000000000000000000\"\n}"
response = http.request(request)
puts response.read_body{
"status": "success",
"data": {
"public_id": "550e8400-e29b-41d4-a716-446655440000",
"state": "authenticated",
"brand_id": "gratona",
"email": "jane@example.org",
"answers": {
"primary_goal": "fundraising_campaigns",
"starting_point": "launching_new"
},
"automatic_billing": false,
"qualification": "verify_payment_method",
"expires_at": "2026-10-21T12:00:00Z",
"first_name": "Jane",
"last_name": "Donor",
"organization_name": "Example Nonprofit",
"website": "https://example.org/",
"ein": "123456789",
"country_code": "US",
"phone": "+12025550123",
"questions": [
{
"key": "primary_goal",
"prompt": "What would you like Gratona to help you set up first?",
"type": "single_choice",
"required": true,
"options": [
{
"value": "fundraising_campaigns",
"label": "Fundraising campaigns and donations"
}
]
}
]
},
"session_token": "example-session-token-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}{
"status": "error",
"code": "workspace_onboarding_session_unavailable",
"message": "This setup session is no longer available. Start again."
}{
"status": "error",
"code": "workspace_onboarding_session_unavailable",
"message": "This setup session is no longer available. Start again."
}{
"status": "error",
"code": "workspace_onboarding_session_unavailable",
"message": "This setup session is no longer available. Start again."
}