curl --request POST \
--url https://api.paywithlocus.com/api/credits/tenants/me/keys/rotate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"expiresIn": 123,
"scopes": [],
"name": "<string>",
"pricing": {
"markupBps": 5000,
"flatMarkupUsdc": "<string>",
"endpoints": {}
},
"gracePeriodSeconds": 1
}
'import requests
url = "https://api.paywithlocus.com/api/credits/tenants/me/keys/rotate"
payload = {
"expiresIn": 123,
"scopes": [],
"name": "<string>",
"pricing": {
"markupBps": 5000,
"flatMarkupUsdc": "<string>",
"endpoints": {}
},
"gracePeriodSeconds": 1
}
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({
expiresIn: 123,
scopes: [],
name: '<string>',
pricing: {markupBps: 5000, flatMarkupUsdc: '<string>', endpoints: {}},
gracePeriodSeconds: 1
})
};
fetch('https://api.paywithlocus.com/api/credits/tenants/me/keys/rotate', 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.paywithlocus.com/api/credits/tenants/me/keys/rotate",
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([
'expiresIn' => 123,
'scopes' => [
],
'name' => '<string>',
'pricing' => [
'markupBps' => 5000,
'flatMarkupUsdc' => '<string>',
'endpoints' => [
]
],
'gracePeriodSeconds' => 1
]),
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.paywithlocus.com/api/credits/tenants/me/keys/rotate"
payload := strings.NewReader("{\n \"expiresIn\": 123,\n \"scopes\": [],\n \"name\": \"<string>\",\n \"pricing\": {\n \"markupBps\": 5000,\n \"flatMarkupUsdc\": \"<string>\",\n \"endpoints\": {}\n },\n \"gracePeriodSeconds\": 1\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.paywithlocus.com/api/credits/tenants/me/keys/rotate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"expiresIn\": 123,\n \"scopes\": [],\n \"name\": \"<string>\",\n \"pricing\": {\n \"markupBps\": 5000,\n \"flatMarkupUsdc\": \"<string>\",\n \"endpoints\": {}\n },\n \"gracePeriodSeconds\": 1\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.paywithlocus.com/api/credits/tenants/me/keys/rotate")
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 \"expiresIn\": 123,\n \"scopes\": [],\n \"name\": \"<string>\",\n \"pricing\": {\n \"markupBps\": 5000,\n \"flatMarkupUsdc\": \"<string>\",\n \"endpoints\": {}\n },\n \"gracePeriodSeconds\": 1\n}"
response = http.request(request)
puts response.read_bodyRotate every active key of a kind (cutover)
Creates ONE replacement and supersedes ALL other active keys of that kind. Requires the workspace owner with credentials:manage and MFA or passkey step-up completed in the last ten minutes. For rotating a single key while its siblings keep working, use POST /credits/tenants/me/keys//rotate.
curl --request POST \
--url https://api.paywithlocus.com/api/credits/tenants/me/keys/rotate \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"expiresIn": 123,
"scopes": [],
"name": "<string>",
"pricing": {
"markupBps": 5000,
"flatMarkupUsdc": "<string>",
"endpoints": {}
},
"gracePeriodSeconds": 1
}
'import requests
url = "https://api.paywithlocus.com/api/credits/tenants/me/keys/rotate"
payload = {
"expiresIn": 123,
"scopes": [],
"name": "<string>",
"pricing": {
"markupBps": 5000,
"flatMarkupUsdc": "<string>",
"endpoints": {}
},
"gracePeriodSeconds": 1
}
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({
expiresIn: 123,
scopes: [],
name: '<string>',
pricing: {markupBps: 5000, flatMarkupUsdc: '<string>', endpoints: {}},
gracePeriodSeconds: 1
})
};
fetch('https://api.paywithlocus.com/api/credits/tenants/me/keys/rotate', 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.paywithlocus.com/api/credits/tenants/me/keys/rotate",
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([
'expiresIn' => 123,
'scopes' => [
],
'name' => '<string>',
'pricing' => [
'markupBps' => 5000,
'flatMarkupUsdc' => '<string>',
'endpoints' => [
]
],
'gracePeriodSeconds' => 1
]),
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.paywithlocus.com/api/credits/tenants/me/keys/rotate"
payload := strings.NewReader("{\n \"expiresIn\": 123,\n \"scopes\": [],\n \"name\": \"<string>\",\n \"pricing\": {\n \"markupBps\": 5000,\n \"flatMarkupUsdc\": \"<string>\",\n \"endpoints\": {}\n },\n \"gracePeriodSeconds\": 1\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.paywithlocus.com/api/credits/tenants/me/keys/rotate")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"expiresIn\": 123,\n \"scopes\": [],\n \"name\": \"<string>\",\n \"pricing\": {\n \"markupBps\": 5000,\n \"flatMarkupUsdc\": \"<string>\",\n \"endpoints\": {}\n },\n \"gracePeriodSeconds\": 1\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.paywithlocus.com/api/credits/tenants/me/keys/rotate")
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 \"expiresIn\": 123,\n \"scopes\": [],\n \"name\": \"<string>\",\n \"pricing\": {\n \"markupBps\": 5000,\n \"flatMarkupUsdc\": \"<string>\",\n \"endpoints\": {}\n },\n \"gracePeriodSeconds\": 1\n}"
response = http.request(request)
puts response.read_bodyAuthorizations
Locus Pro dashboard session (Cognito).
Body
secret, publishable, sandbox Lifetime of the new key in seconds. Omit for the default.
Scopes for the new key. Omit to keep the kind's defaults. Publishable keys accept only widget:read.
tenant:read, tenant:write, catalog:write, credits:move, credentials:manage, tokens:manage, payouts:manage, members:manage, widget:read Name for the replacement key.
1 - 100Endpoint allowlist for the replacement; makes it a burn-only execution credential.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Keep the old key valid this long for zero-downtime cutover. Omitted means the old key dies with this response.
x >= 0Response
{ success, kind, key, keyPrefix } — the raw key is returned exactly once. Unlike create and per-key rotation, this cutover endpoint returns keyPrefix rather than a keyMetadata object. Without gracePeriodSeconds the superseded keys stop working with this response; with it they keep authenticating until the overlap ends.