curl --request POST \
--url https://app.textql.com/v2/api-keys \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"expirySeconds": 3600,
"assumedRoles": [
"80de0196-496f-44fe-9d4c-8013b3b44082"
],
"inheritAllRoles": true,
"name": "acme-session-key",
"targetMemberId": "<string>",
"clientId": "{\"tenant_id\": \"acme\", \"user_email\": \"jane@acme.example\"}",
"fullMemberAccess": true
}
'import requests
url = "https://app.textql.com/v2/api-keys"
payload = {
"expirySeconds": 3600,
"assumedRoles": ["80de0196-496f-44fe-9d4c-8013b3b44082"],
"inheritAllRoles": True,
"name": "acme-session-key",
"targetMemberId": "<string>",
"clientId": "{\"tenant_id\": \"acme\", \"user_email\": \"jane@acme.example\"}",
"fullMemberAccess": True
}
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({
expirySeconds: 3600,
assumedRoles: ['80de0196-496f-44fe-9d4c-8013b3b44082'],
inheritAllRoles: true,
name: 'acme-session-key',
targetMemberId: '<string>',
clientId: '{"tenant_id": "acme", "user_email": "jane@acme.example"}',
fullMemberAccess: true
})
};
fetch('https://app.textql.com/v2/api-keys', 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://app.textql.com/v2/api-keys",
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([
'expirySeconds' => 3600,
'assumedRoles' => [
'80de0196-496f-44fe-9d4c-8013b3b44082'
],
'inheritAllRoles' => true,
'name' => 'acme-session-key',
'targetMemberId' => '<string>',
'clientId' => '{"tenant_id": "acme", "user_email": "jane@acme.example"}',
'fullMemberAccess' => true
]),
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://app.textql.com/v2/api-keys"
payload := strings.NewReader("{\n \"expirySeconds\": 3600,\n \"assumedRoles\": [\n \"80de0196-496f-44fe-9d4c-8013b3b44082\"\n ],\n \"inheritAllRoles\": true,\n \"name\": \"acme-session-key\",\n \"targetMemberId\": \"<string>\",\n \"clientId\": \"{\\\"tenant_id\\\": \\\"acme\\\", \\\"user_email\\\": \\\"jane@acme.example\\\"}\",\n \"fullMemberAccess\": true\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://app.textql.com/v2/api-keys")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"expirySeconds\": 3600,\n \"assumedRoles\": [\n \"80de0196-496f-44fe-9d4c-8013b3b44082\"\n ],\n \"inheritAllRoles\": true,\n \"name\": \"acme-session-key\",\n \"targetMemberId\": \"<string>\",\n \"clientId\": \"{\\\"tenant_id\\\": \\\"acme\\\", \\\"user_email\\\": \\\"jane@acme.example\\\"}\",\n \"fullMemberAccess\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.textql.com/v2/api-keys")
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 \"expirySeconds\": 3600,\n \"assumedRoles\": [\n \"80de0196-496f-44fe-9d4c-8013b3b44082\"\n ],\n \"inheritAllRoles\": true,\n \"name\": \"acme-session-key\",\n \"targetMemberId\": \"<string>\",\n \"clientId\": \"{\\\"tenant_id\\\": \\\"acme\\\", \\\"user_email\\\": \\\"jane@acme.example\\\"}\",\n \"fullMemberAccess\": true\n}"
response = http.request(request)
puts response.read_body{
"key": "BEARER_SECRET_SHOWN_ONCE",
"api_key": {
"id": "4f0c39a1-7c9e-4a34-9d20-6a1f6f6f2b71",
"member_id": "9b2f7a64-11d0-4c1b-8f3e-2f9c5b7f6e10",
"name": "acme-session-key",
"client_id": "{\"tenant_id\": \"acme\", \"user_email\": \"jane@acme.example\"}",
"assumed_roles": [
"80de0196-496f-44fe-9d4c-8013b3b44082"
],
"status": "active",
"created_at": "2026-07-06T19:30:00Z",
"expires_at": "2026-07-06T20:30:00Z"
}
}{
"error": {
"code": "invalid_request",
"message": "Invalid request body"
}
}{
"error": {
"code": "unauthenticated",
"message": "Authentication required"
}
}{
"error": {
"code": "permission_denied",
"message": "Insufficient permissions"
}
}{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded"
}
}{
"error": {
"code": "internal",
"message": "Internal server error"
}
}Create API Key
Mint a role-scoped platform API key, optionally carrying client metadata for row-level security
curl --request POST \
--url https://app.textql.com/v2/api-keys \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"expirySeconds": 3600,
"assumedRoles": [
"80de0196-496f-44fe-9d4c-8013b3b44082"
],
"inheritAllRoles": true,
"name": "acme-session-key",
"targetMemberId": "<string>",
"clientId": "{\"tenant_id\": \"acme\", \"user_email\": \"jane@acme.example\"}",
"fullMemberAccess": true
}
'import requests
url = "https://app.textql.com/v2/api-keys"
payload = {
"expirySeconds": 3600,
"assumedRoles": ["80de0196-496f-44fe-9d4c-8013b3b44082"],
"inheritAllRoles": True,
"name": "acme-session-key",
"targetMemberId": "<string>",
"clientId": "{\"tenant_id\": \"acme\", \"user_email\": \"jane@acme.example\"}",
"fullMemberAccess": True
}
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({
expirySeconds: 3600,
assumedRoles: ['80de0196-496f-44fe-9d4c-8013b3b44082'],
inheritAllRoles: true,
name: 'acme-session-key',
targetMemberId: '<string>',
clientId: '{"tenant_id": "acme", "user_email": "jane@acme.example"}',
fullMemberAccess: true
})
};
fetch('https://app.textql.com/v2/api-keys', 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://app.textql.com/v2/api-keys",
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([
'expirySeconds' => 3600,
'assumedRoles' => [
'80de0196-496f-44fe-9d4c-8013b3b44082'
],
'inheritAllRoles' => true,
'name' => 'acme-session-key',
'targetMemberId' => '<string>',
'clientId' => '{"tenant_id": "acme", "user_email": "jane@acme.example"}',
'fullMemberAccess' => true
]),
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://app.textql.com/v2/api-keys"
payload := strings.NewReader("{\n \"expirySeconds\": 3600,\n \"assumedRoles\": [\n \"80de0196-496f-44fe-9d4c-8013b3b44082\"\n ],\n \"inheritAllRoles\": true,\n \"name\": \"acme-session-key\",\n \"targetMemberId\": \"<string>\",\n \"clientId\": \"{\\\"tenant_id\\\": \\\"acme\\\", \\\"user_email\\\": \\\"jane@acme.example\\\"}\",\n \"fullMemberAccess\": true\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://app.textql.com/v2/api-keys")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"expirySeconds\": 3600,\n \"assumedRoles\": [\n \"80de0196-496f-44fe-9d4c-8013b3b44082\"\n ],\n \"inheritAllRoles\": true,\n \"name\": \"acme-session-key\",\n \"targetMemberId\": \"<string>\",\n \"clientId\": \"{\\\"tenant_id\\\": \\\"acme\\\", \\\"user_email\\\": \\\"jane@acme.example\\\"}\",\n \"fullMemberAccess\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.textql.com/v2/api-keys")
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 \"expirySeconds\": 3600,\n \"assumedRoles\": [\n \"80de0196-496f-44fe-9d4c-8013b3b44082\"\n ],\n \"inheritAllRoles\": true,\n \"name\": \"acme-session-key\",\n \"targetMemberId\": \"<string>\",\n \"clientId\": \"{\\\"tenant_id\\\": \\\"acme\\\", \\\"user_email\\\": \\\"jane@acme.example\\\"}\",\n \"fullMemberAccess\": true\n}"
response = http.request(request)
puts response.read_body{
"key": "BEARER_SECRET_SHOWN_ONCE",
"api_key": {
"id": "4f0c39a1-7c9e-4a34-9d20-6a1f6f6f2b71",
"member_id": "9b2f7a64-11d0-4c1b-8f3e-2f9c5b7f6e10",
"name": "acme-session-key",
"client_id": "{\"tenant_id\": \"acme\", \"user_email\": \"jane@acme.example\"}",
"assumed_roles": [
"80de0196-496f-44fe-9d4c-8013b3b44082"
],
"status": "active",
"created_at": "2026-07-06T19:30:00Z",
"expires_at": "2026-07-06T20:30:00Z"
}
}{
"error": {
"code": "invalid_request",
"message": "Invalid request body"
}
}{
"error": {
"code": "unauthenticated",
"message": "Authentication required"
}
}{
"error": {
"code": "permission_denied",
"message": "Insufficient permissions"
}
}{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded"
}
}{
"error": {
"code": "internal",
"message": "Internal server error"
}
}Authorizations
API key or JWT token
Body
Optional TTL in seconds. Omit for a non-expiring key; short-lived keys are recommended for per-session embedding flows.
3600
Role UUIDs to scope the key to. Callers may only specify roles they hold; API-key callers may only specify a subset of their own assumed roles.
["80de0196-496f-44fe-9d4c-8013b3b44082"]
Set to true to inherit all of the creating member's roles. Required when assumedRoles is empty.
Optional display name for the key.
"acme-session-key"
Mint the key for this member instead of the caller. A human target requires the caller's role to hold api_access_key:delegate; organization:write alone is denied. A service-account target requires organization:write. The key authenticates as the target member, always carries full member access so the target's direct object grants stay reachable, is scoped to that member's roles, and suppresses superadmin elevation. Granting api_access_key:delegate is a full impersonation capability.
Optional client metadata stored on the key. Prefer a JSON object string (a JSON-encoded string, not a nested object); TQL row-level security reads its fields as _tql.client_attributes_json.<field>.
"{\"tenant_id\": \"acme\", \"user_email\": \"jane@acme.example\"}"
When true, the key keeps the target member's direct object grants alongside its assumed-role grants. A key minted on behalf of a human member always carries full member access regardless of this flag. For a key acting as the caller or as a service account, false is the default and an API-key caller may set it true only when its own key already has full member access.