curl --request POST \
--url https://api.your-blindsight.com/api/license/upgrade-intent \
--header 'Content-Type: application/json' \
--data '
{
"plan_slug": "<string>"
}
'import requests
url = "https://api.your-blindsight.com/api/license/upgrade-intent"
payload = { "plan_slug": "<string>" }
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({plan_slug: '<string>'})
};
fetch('https://api.your-blindsight.com/api/license/upgrade-intent', 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.your-blindsight.com/api/license/upgrade-intent",
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([
'plan_slug' => '<string>'
]),
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.your-blindsight.com/api/license/upgrade-intent"
payload := strings.NewReader("{\n \"plan_slug\": \"<string>\"\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.your-blindsight.com/api/license/upgrade-intent")
.header("Content-Type", "application/json")
.body("{\n \"plan_slug\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.your-blindsight.com/api/license/upgrade-intent")
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 \"plan_slug\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"url": "<string>"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Create Upgrade Intent
Start a plan purchase and return the URL to send the customer to.
This is the whole of the platform’s involvement in commerce. It attests who
is asking and for which workspace, hands that to the License Service over an
outbound call, and returns the Stripe URL that comes back. It creates no
Stripe session itself, holds no Stripe key, and grants nothing. See
services.upgrade_intent and docs/LICENSING_ARCHITECTURE_PLAN.md.
Deliberately reachable with an expired or absent license: /api/license
is exempt from the lockout gate, because a workspace whose license lapsed
is exactly the one that needs to buy.
curl --request POST \
--url https://api.your-blindsight.com/api/license/upgrade-intent \
--header 'Content-Type: application/json' \
--data '
{
"plan_slug": "<string>"
}
'import requests
url = "https://api.your-blindsight.com/api/license/upgrade-intent"
payload = { "plan_slug": "<string>" }
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({plan_slug: '<string>'})
};
fetch('https://api.your-blindsight.com/api/license/upgrade-intent', 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.your-blindsight.com/api/license/upgrade-intent",
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([
'plan_slug' => '<string>'
]),
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.your-blindsight.com/api/license/upgrade-intent"
payload := strings.NewReader("{\n \"plan_slug\": \"<string>\"\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.your-blindsight.com/api/license/upgrade-intent")
.header("Content-Type", "application/json")
.body("{\n \"plan_slug\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.your-blindsight.com/api/license/upgrade-intent")
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 \"plan_slug\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"url": "<string>"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Body
Which plan the caller wants. The workspace is NOT a field: it comes from the authenticated session, never from the client.
1 - 64Response
Successful Response
Just somewhere to send the browser. The signed intent is never returned to a client: it is exchanged server-side.

