curl --request POST \
--url https://api.circuit.org/partner/v0.1/links \
--header 'Content-Type: application/json' \
--data '
{
"app": "pinewood",
"public_key": "8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd",
"return_url": "https://yourapp.com/linked"
}
'import requests
url = "https://api.circuit.org/partner/v0.1/links"
payload = {
"app": "pinewood",
"public_key": "8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd",
"return_url": "https://yourapp.com/linked"
}
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({
app: 'pinewood',
public_key: '8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd',
return_url: 'https://yourapp.com/linked'
})
};
fetch('https://api.circuit.org/partner/v0.1/links', 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.circuit.org/partner/v0.1/links",
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([
'app' => 'pinewood',
'public_key' => '8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd',
'return_url' => 'https://yourapp.com/linked'
]),
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.circuit.org/partner/v0.1/links"
payload := strings.NewReader("{\n \"app\": \"pinewood\",\n \"public_key\": \"8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd\",\n \"return_url\": \"https://yourapp.com/linked\"\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.circuit.org/partner/v0.1/links")
.header("Content-Type", "application/json")
.body("{\n \"app\": \"pinewood\",\n \"public_key\": \"8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd\",\n \"return_url\": \"https://yourapp.com/linked\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.circuit.org/partner/v0.1/links")
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 \"app\": \"pinewood\",\n \"public_key\": \"8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd\",\n \"return_url\": \"https://yourapp.com/linked\"\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"url": "<string>",
"status": "waiting",
"expires_at": "2023-11-07T05:31:56Z",
"permit": "<string>"
}{
"error": "<string>",
"code": "<string>"
}Create a link
Creates a page where the user signs in to Circuit, picks which wallets your app may use and approves with their passkey. Open the returned url in a real browser, then check the link until it is approved. A link expires after five minutes. Needs no permit.
curl --request POST \
--url https://api.circuit.org/partner/v0.1/links \
--header 'Content-Type: application/json' \
--data '
{
"app": "pinewood",
"public_key": "8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd",
"return_url": "https://yourapp.com/linked"
}
'import requests
url = "https://api.circuit.org/partner/v0.1/links"
payload = {
"app": "pinewood",
"public_key": "8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd",
"return_url": "https://yourapp.com/linked"
}
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({
app: 'pinewood',
public_key: '8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd',
return_url: 'https://yourapp.com/linked'
})
};
fetch('https://api.circuit.org/partner/v0.1/links', 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.circuit.org/partner/v0.1/links",
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([
'app' => 'pinewood',
'public_key' => '8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd',
'return_url' => 'https://yourapp.com/linked'
]),
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.circuit.org/partner/v0.1/links"
payload := strings.NewReader("{\n \"app\": \"pinewood\",\n \"public_key\": \"8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd\",\n \"return_url\": \"https://yourapp.com/linked\"\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.circuit.org/partner/v0.1/links")
.header("Content-Type", "application/json")
.body("{\n \"app\": \"pinewood\",\n \"public_key\": \"8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd\",\n \"return_url\": \"https://yourapp.com/linked\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.circuit.org/partner/v0.1/links")
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 \"app\": \"pinewood\",\n \"public_key\": \"8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd\",\n \"return_url\": \"https://yourapp.com/linked\"\n}"
response = http.request(request)
puts response.read_body{
"id": "<string>",
"url": "<string>",
"status": "waiting",
"expires_at": "2023-11-07T05:31:56Z",
"permit": "<string>"
}{
"error": "<string>",
"code": "<string>"
}Body
Your app id, as Circuit registered it. The user sees your app's name when they approve.
"pinewood"
Your X25519 public key as 64 hex characters. Circuit encrypts the permit to this key.
"8f2c1d6e4b3a59078c1e2d3f4a5b6c7d8e9f00112233445566778899aabbccdd"
Where Circuit sends the user after they approve. It must start with one of the URLs registered for your app. Optional.
"https://yourapp.com/linked"
Response
OK
A page where the user approves your app. Once they do, it holds their permit.
The link's id, starting with lnk_.
Open this in a real browser so the user can sign in, pick wallets and approve.
The user has not approved yet.
"waiting"When the link expires, five minutes after it was created.
The user's permit, encrypted to your public key, once status is approved. It is base64url of a 32-byte ephemeral X25519 public key, a 12-byte IV and the AES-256-GCM ciphertext. Null until then.