const res = await fetch('https://app.trdrs.co/api/partner/webhooks', {
method: 'POST',
headers: {
'content-type': 'application/json',
Authorization: `Bearer ${process.env.TRDRS_API_KEY}`,
},
body: JSON.stringify({
"url": "https://ops.yourfirm.example/hooks/trdrs",
"events": [
"registration.linked",
"risk.locked",
"balance.recorded"
]
}),
})
const data = await res.json()curl -X POST 'https://app.trdrs.co/api/partner/webhooks' \
-H "Authorization: Bearer $TRDRS_API_KEY" \
-H 'content-type: application/json' \
-d '{"url":"https://ops.yourfirm.example/hooks/trdrs","events":["registration.linked","risk.locked","balance.recorded"]}'import requests
url = "https://app.trdrs.co/api/partner/webhooks"
payload = {
"url": "https://ops.yourfirm.example/hooks/trdrs",
"events": ["registration.linked", "risk.locked", "balance.recorded"]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.trdrs.co/api/partner/webhooks",
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([
'url' => 'https://ops.yourfirm.example/hooks/trdrs',
'events' => [
'registration.linked',
'risk.locked',
'balance.recorded'
]
]),
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.trdrs.co/api/partner/webhooks"
payload := strings.NewReader("{\n \"url\": \"https://ops.yourfirm.example/hooks/trdrs\",\n \"events\": [\n \"registration.linked\",\n \"risk.locked\",\n \"balance.recorded\"\n ]\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.trdrs.co/api/partner/webhooks")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"https://ops.yourfirm.example/hooks/trdrs\",\n \"events\": [\n \"registration.linked\",\n \"risk.locked\",\n \"balance.recorded\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.trdrs.co/api/partner/webhooks")
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 \"url\": \"https://ops.yourfirm.example/hooks/trdrs\",\n \"events\": [\n \"registration.linked\",\n \"risk.locked\",\n \"balance.recorded\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"webhook": {
"id": "whk_4d19c2",
"url": "https://ops.yourfirm.example/hooks/trdrs",
"events": [
"registration.linked",
"risk.locked",
"balance.recorded"
],
"enabled": true,
"secret": "trdrs_whsec_9Kb2xR7pQm4TvJhN6sYcAeWd",
"createdAt": "2026-08-24T18:02:00Z"
}
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}Create a webhook
Registers an https endpoint; the platform POSTs events to it as they happen. Omit events to receive everything, including event types added later; name a subset to filter. Five endpoints per firm.
The event catalog today: registration.linked, registration.revoked, account.reset, balance.recorded, risk.locked, risk.unlocked.
The response carries the signing secret — it is shown on every read, not once, because it authenticates us to your endpoint and grants no access here. Verify every delivery with it:
import { createHmac, timingSafeEqual } from 'node:crypto'
// rawBody is the exact bytes we sent — verify before JSON.parse, not after.
function verify(secret: string, header: string, rawBody: string): boolean {
const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header)
if (!m) return false
if (Math.abs(Math.floor(Date.now() / 1000) - Number(m[1])) > 300) return false // stale
const expected = createHmac('sha256', secret).update(`${m[1]}.${rawBody}`).digest()
const given = Buffer.from(m[2], 'hex')
return expected.length === given.length && timingSafeEqual(expected, given)
}
Answer 2xx to accept a delivery. Anything else is retried with backoff (about nine hours in total), so your endpoint may see the same event twice — key your handler on the delivery id.
const res = await fetch('https://app.trdrs.co/api/partner/webhooks', {
method: 'POST',
headers: {
'content-type': 'application/json',
Authorization: `Bearer ${process.env.TRDRS_API_KEY}`,
},
body: JSON.stringify({
"url": "https://ops.yourfirm.example/hooks/trdrs",
"events": [
"registration.linked",
"risk.locked",
"balance.recorded"
]
}),
})
const data = await res.json()curl -X POST 'https://app.trdrs.co/api/partner/webhooks' \
-H "Authorization: Bearer $TRDRS_API_KEY" \
-H 'content-type: application/json' \
-d '{"url":"https://ops.yourfirm.example/hooks/trdrs","events":["registration.linked","risk.locked","balance.recorded"]}'import requests
url = "https://app.trdrs.co/api/partner/webhooks"
payload = {
"url": "https://ops.yourfirm.example/hooks/trdrs",
"events": ["registration.linked", "risk.locked", "balance.recorded"]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.trdrs.co/api/partner/webhooks",
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([
'url' => 'https://ops.yourfirm.example/hooks/trdrs',
'events' => [
'registration.linked',
'risk.locked',
'balance.recorded'
]
]),
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.trdrs.co/api/partner/webhooks"
payload := strings.NewReader("{\n \"url\": \"https://ops.yourfirm.example/hooks/trdrs\",\n \"events\": [\n \"registration.linked\",\n \"risk.locked\",\n \"balance.recorded\"\n ]\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.trdrs.co/api/partner/webhooks")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"https://ops.yourfirm.example/hooks/trdrs\",\n \"events\": [\n \"registration.linked\",\n \"risk.locked\",\n \"balance.recorded\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.trdrs.co/api/partner/webhooks")
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 \"url\": \"https://ops.yourfirm.example/hooks/trdrs\",\n \"events\": [\n \"registration.linked\",\n \"risk.locked\",\n \"balance.recorded\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"webhook": {
"id": "whk_4d19c2",
"url": "https://ops.yourfirm.example/hooks/trdrs",
"events": [
"registration.linked",
"risk.locked",
"balance.recorded"
],
"enabled": true,
"secret": "trdrs_whsec_9Kb2xR7pQm4TvJhN6sYcAeWd",
"createdAt": "2026-08-24T18:02:00Z"
}
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}{
"error": "invalid_instrument"
}Authorizations
A partner-scoped API key (trdrs_sk_…), issued to a trdrs Connect partner firm and accepted only under /api/partner/. Same format as the firm (tenant) key, different scope: a firm API key is refused here, and this key is refused everywhere else.
Body
PartnerWebhookCreateRequest. Register one endpoint. Omit events to receive everything; five endpoints per firm.
An https:// URL (≤500 chars). Plain http is refused, and a host resolving to a private address is refused at delivery time.
Optional filter. Omitted or empty = every event type, including ones added later.
registration.linked, registration.revoked, account.reset, balance.recorded, risk.locked, risk.unlocked Response
The registered endpoint, including its signing secret (PartnerWebhookResponse)
PartnerWebhook. One registered endpoint. secret is returned on every read on purpose: it authenticates us to your endpoint and grants nothing here, and the only reader is your own partner key.
Show child attributes
Show child attributes
{
"id": "whk_4d19c2",
"url": "https://ops.yourfirm.example/hooks/trdrs",
"events": [
"registration.linked",
"risk.locked",
"balance.recorded"
],
"enabled": true,
"secret": "trdrs_whsec_9Kb2xR7pQm4TvJhN6sYcAeWd",
"createdAt": "2026-08-24T18:02:00Z"
}