Skip to main content
Create webhook
curl --request POST \
  --url https://customer-api.fortary.io/v1/webhooks
import requests

url = "https://customer-api.fortary.io/v1/webhooks"

response = requests.post(url)

print(response.text)
const options = {method: 'POST'};

fetch('https://customer-api.fortary.io/v1/webhooks', 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://customer-api.fortary.io/v1/webhooks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"net/http"
"io"
)

func main() {

url := "https://customer-api.fortary.io/v1/webhooks"

req, _ := http.NewRequest("POST", url, nil)

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.post("https://customer-api.fortary.io/v1/webhooks")
.asString();
require 'uri'
require 'net/http'

url = URI("https://customer-api.fortary.io/v1/webhooks")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)

response = http.request(request)
puts response.read_body
Pre-release — contact your Fortary account team for access.
Illustrative reference. These examples show the shape of the API and are provided for early evaluation while it is still being finalized. Field names and response details may change before the API is released. A complete reference, generated directly from the live API, will replace these examples once it is available.
Creates a webhook. Requires the webhook:manage scope.

Request

curl -X POST https://customer-api.fortary.io/v1/webhooks \
  -H "Authorization: Bearer fort_..." \
  -H "Content-Type: application/json" \
  -d '{
    "entityIds": ["entity_7c1e4a0c2f"],
    "url": "https://example.com/fortary/webhooks",
    "eventKinds": ["transaction.confirmed", "transaction.failed"],
    "vaultIds": null,
    "notificationEmail": "[email protected]",
    "description": "Treasury reconciliation"
  }'
  • entityIds — the entities this webhook covers (at least one).
  • url — the HTTPS endpoint that receives signed POSTs.
  • eventKinds — exact event kinds to deliver; no wildcards.
  • vaultIds (optional) — vault allowlist for vault-scoped events; null means all vaults.
  • notificationEmail (optional) — recipient of operational notices; defaults to the key owner’s email.
  • description (optional) — free-text label, max 500 characters.

Response

{
  "id": "whs_9b2f4e1a7c",
  "entityIds": ["entity_7c1e4a0c2f"],
  "url": "https://example.com/fortary/webhooks",
  "notificationEmail": "[email protected]",
  "eventKinds": ["transaction.confirmed", "transaction.failed"],
  "vaultIds": null,
  "status": "ENABLED",
  "disabledReason": null,
  "consecutiveExhaustedCount": 0,
  "lastDeliveredAt": null,
  "description": "Treasury reconciliation",
  "createdAt": "2026-06-10T09:12:44Z",
  "updatedAt": "2026-06-10T09:12:44Z",
  "secret": "whsec_mB4tQ9..."
}
The webhook fields are described in Get webhook.
secret is returned exactly once — here and on rotation. Store it in your secret manager now; it cannot be retrieved later.
An entity can be covered by at most 20 active webhooks; creating one beyond the cap returns 409. Errors follow the standard error envelope.