curl --request POST \
--url https://openrouter.ai/api/v1/interns/{internId}/daemon/sign \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"bodySha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
'import requests
url = "https://openrouter.ai/api/v1/interns/{internId}/daemon/sign"
payload = { "bodySha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }
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({bodySha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'})
};
fetch('https://openrouter.ai/api/v1/interns/{internId}/daemon/sign', 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://openrouter.ai/api/v1/interns/{internId}/daemon/sign",
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([
'bodySha256' => 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
]),
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://openrouter.ai/api/v1/interns/{internId}/daemon/sign"
payload := strings.NewReader("{\n \"bodySha256\": \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"\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://openrouter.ai/api/v1/interns/{internId}/daemon/sign")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"bodySha256\": \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://openrouter.ai/api/v1/interns/{internId}/daemon/sign")
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 \"bodySha256\": \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"\n}"
response = http.request(request)
puts response.read_body{
"x-ori-invoke-signature": "MEUCIQ...",
"x-ori-invoke-timestamp": "1789000000",
"x-ori-invoke-user": "user_2abc"
}{
"error": {
"code": 400,
"message": "Invalid request body",
"metadata": {
"reason": "invalid_body",
"retryable": false
}
}
}{
"error": {
"code": 401,
"message": "Invalid or missing API key"
}
}{
"error": {
"code": 403,
"message": "Personal connections require ori login --oidc.",
"metadata": {
"reason": "personal_identity_required",
"retryable": false
}
}
}{
"error": {
"code": 404,
"message": "Intern not found",
"metadata": {
"reason": "not_found",
"retryable": false
}
}
}{
"error": {
"code": 408,
"message": "Operation timed out after 10s. Please try again later.",
"metadata": {
"reason": "timeout",
"retryable": true
}
}
}{
"error": {
"code": 413,
"message": "Request body exceeds 1048576 bytes",
"metadata": {
"reason": "payload_too_large",
"retryable": false
}
}
}{
"error": {
"code": 415,
"message": "Request body must be sent as application/json",
"metadata": {
"reason": "unsupported_media_type",
"retryable": false
}
}
}{
"error": {
"code": 429,
"message": "Too many intern turns. Please wait a moment.",
"metadata": {
"reason": "rate_limited",
"retryable": true
}
}
}{
"error": {
"code": 500,
"message": "The request could not be completed",
"metadata": {
"reason": "internal_error",
"retryable": true
}
}
}Sign a daemon request with the caller's identity
Signs the SHA-256 digest of one request the CLI is about to send to the intern daemon, binding it to the intern and to the signed-in member so personal connections resolve. Only an OAuth session from ori login --oidc whose grant carries vault:read can sign: an API key is refused with 403 because it names no person, and an interns-only grant is refused with 403 because a proof releases that user’s personal connections. The route is behind the same gate as chat and counts against the chat turn limiter. The response is sent with Cache-Control: no-store. The API key selects the caller, workspace and visible interns. An intern’s own API key sees only that intern: the collection and every other intern answer 404 to it. There is no default workspace fallback. Requests on regional hostnames such as eu.openrouter.ai are refused. API key required.
curl --request POST \
--url https://openrouter.ai/api/v1/interns/{internId}/daemon/sign \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"bodySha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
'import requests
url = "https://openrouter.ai/api/v1/interns/{internId}/daemon/sign"
payload = { "bodySha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }
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({bodySha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'})
};
fetch('https://openrouter.ai/api/v1/interns/{internId}/daemon/sign', 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://openrouter.ai/api/v1/interns/{internId}/daemon/sign",
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([
'bodySha256' => 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
]),
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://openrouter.ai/api/v1/interns/{internId}/daemon/sign"
payload := strings.NewReader("{\n \"bodySha256\": \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"\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://openrouter.ai/api/v1/interns/{internId}/daemon/sign")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"bodySha256\": \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://openrouter.ai/api/v1/interns/{internId}/daemon/sign")
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 \"bodySha256\": \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"\n}"
response = http.request(request)
puts response.read_body{
"x-ori-invoke-signature": "MEUCIQ...",
"x-ori-invoke-timestamp": "1789000000",
"x-ori-invoke-user": "user_2abc"
}{
"error": {
"code": 400,
"message": "Invalid request body",
"metadata": {
"reason": "invalid_body",
"retryable": false
}
}
}{
"error": {
"code": 401,
"message": "Invalid or missing API key"
}
}{
"error": {
"code": 403,
"message": "Personal connections require ori login --oidc.",
"metadata": {
"reason": "personal_identity_required",
"retryable": false
}
}
}{
"error": {
"code": 404,
"message": "Intern not found",
"metadata": {
"reason": "not_found",
"retryable": false
}
}
}{
"error": {
"code": 408,
"message": "Operation timed out after 10s. Please try again later.",
"metadata": {
"reason": "timeout",
"retryable": true
}
}
}{
"error": {
"code": 413,
"message": "Request body exceeds 1048576 bytes",
"metadata": {
"reason": "payload_too_large",
"retryable": false
}
}
}{
"error": {
"code": 415,
"message": "Request body must be sent as application/json",
"metadata": {
"reason": "unsupported_media_type",
"retryable": false
}
}
}{
"error": {
"code": 429,
"message": "Too many intern turns. Please wait a moment.",
"metadata": {
"reason": "rate_limited",
"retryable": true
}
}
}{
"error": {
"code": 500,
"message": "The request could not be completed",
"metadata": {
"reason": "internal_error",
"retryable": true
}
}
}Authorizations
API key as bearer token in Authorization header
Path Parameters
ID of an intern visible to the authenticated API key.
1"7c9e6679-7425-40de-944b-e07fc1f90ae7"
Body
Lower-case hex SHA-256 of the exact bytes the CLI will send as the daemon request body.
^[0-9a-f]{64}$"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
Response
Signature headers for the digest.
Headers the CLI copies onto the daemon request so the sidecar can verify who is asking.
Base64 Ed25519 signature over the intern id, timestamp, user and body digest.
Unix seconds at signing; the daemon refuses proofs older than its window.
The verified OAuth subject the proof names. Never taken from the request.