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 a workspace owner or admin with credentials:manage and MFA or passkey step-up completed in the last ten minutes. A human caller cannot delegate a secret-key scope absent from their own effective membership scopes. 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). High-risk workspace administration requires an owner or admin role and fresh MFA/passkey step-up.
Headers
Short-lived raw MFA/passkey step-up token bound to the authenticated Cognito subject. Send it alongside the dashboard Bearer token for workspace-administrator mutations when the base session does not itself prove MFA within the last ten minutes. Never send it with a tenant secret key.
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.