Appearance
Exchange a connect code for a site key
- Method:
POST - Path:
/v1/sites/exchange - Operation ID:
exchangeSiteCode - Tags: Connected sites
For the show.fm WordPress plugin, server to server, after the browser returns to wp-admin with code and state. No API key: the one-time code is the credential, and it is bound to the S256 challenge the connection started with. A code works once, for five minutes; any attempt uses it up. Answers the site key, the ping secret and the expiry once, never cached. Limited to 30 requests a minute per IP address.
Make a request
Set these environment variables before running a sample. Each language reads the same values; no identifier needs replacing inside the code.
| Variable | Value to supply |
|---|---|
CONNECT_CODE | The code from the redirect back to wp-admin. |
CODE_VERIFIER | The code verifier the plugin stored when the connection started. |
Samples run on your server: curl 7.76+, Node.js 22+ (TypeScript via your TypeScript runner), Python 3, or PHP 8 with the cURL extension.
bash
: "${CONNECT_CODE:?Set CONNECT_CODE in your environment}"
: "${CODE_VERIFIER:?Set CODE_VERIFIER in your environment}"
curl --fail-with-body --silent --show-error --request POST \
"https://api.show.fm/v1/sites/exchange" \
--header "Content-Type: application/json" \
--data "{\"code\":\"${CONNECT_CODE}\",\"code_verifier\":\"${CODE_VERIFIER}\"}"ts
function env(name: string): string {
const value = process.env[name];
if (!value) throw new Error(`Set ${name} in your environment`);
return value;
}
async function main() {
const url = new URL(`https://api.show.fm/v1/sites/exchange`);
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
code: env("CONNECT_CODE"),
code_verifier: env("CODE_VERIFIER")
}),
signal: AbortSignal.timeout(30_000)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
console.log(JSON.stringify(await response.json(), null, 2));
}
main().catch(error => {
console.error(error);
process.exitCode = 1;
});js
function env(name) {
const value = process.env[name];
if (!value) throw new Error(`Set ${name} in your environment`);
return value;
}
async function main() {
const url = new URL(`https://api.show.fm/v1/sites/exchange`);
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
code: env("CONNECT_CODE"),
code_verifier: env("CODE_VERIFIER")
}),
signal: AbortSignal.timeout(30_000)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
console.log(JSON.stringify(await response.json(), null, 2));
}
main().catch(error => {
console.error(error);
process.exitCode = 1;
});python
import os
import json
from urllib.parse import quote, urlencode
from urllib.request import Request, urlopen
from urllib.error import HTTPError
url = f'https://api.show.fm/v1/sites/exchange'
body = json.dumps({
"code": os.environ["CONNECT_CODE"],
"code_verifier": os.environ["CODE_VERIFIER"],
}).encode()
request = Request(url, data=body, method="POST", headers={
"Content-Type": "application/json"
})
try:
with urlopen(request, timeout=30) as response:
print(json.dumps(json.load(response), indent=2))
except HTTPError as error:
raise RuntimeError(f"HTTP {error.code}: {error.read().decode()}") from errorphp
<?php
function requiredEnv(string $name): string {
$value = getenv($name);
if ($value === false || $value === '') {
throw new RuntimeException("Set $name in your environment");
}
return $value;
}
$url = "https://api.show.fm" . "/v1/sites/exchange";
$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode([
"code" => requiredEnv("CONNECT_CODE"),
"code_verifier" => requiredEnv("CODE_VERIFIER"),
], JSON_THROW_ON_ERROR),
]);
$body = curl_exec($curl);
if ($body === false) {
throw new RuntimeException(curl_error($curl));
}
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($status < 200 || $status >= 300) {
throw new RuntimeException("HTTP $status: $body");
}
echo json_encode(json_decode($body, false, 512, JSON_THROW_ON_ERROR), JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR) . PHP_EOL;Base URL
https://api.show.fm
Authentication
No authentication required.
Request body
Required: true
Content type: application/json
schema: SiteExchangeRequest, no additional properties
code(required):string, pattern:^[A-Za-z0-9_-]{43}$The one-time code from the redirect back to wp-admin. Single use, valid for five minutes.
code_verifier(required):string, pattern:^[A-Za-z0-9._~-]{43,128}$The verifier whose S256 challenge started the connection.
Responses
Browse the documented response statuses, headers and body fields.
200 OK
400, 429, 503
400invalid_request: a malformed body, or a code that is invalid, used or expired.429rate_limited. Wait forRetry-Afterseconds.503service_unavailable. Retry shortly.
Response body
Content type: application/json
schema: Error, no additional properties
error(required):object, no additional propertiescode(required):string, possible values:"invalid_request", "not_found", "unavailable", "unauthorized", "invalid_api_key", "insufficient_scope", "forbidden", "plan_upgrade_required", "rate_limited", "service_unavailable", "internal_error", "not_implemented"message(required):stringdetails:arrayItems:
no additional properties
field(required):stringmessage(required):stringreason:stringA stable reason a client may branch on, for example
outside_retention.
docs_url:stringrequest_id:string