Nyria Docs
Alert sources

Use a Custom Webhook Source

Wire any service that can POST HTTP to Nyria. By the end you will have a working custom webhook firing real orders through your connected broker.

By the end you will have a custom-built script (or third-party service) firing alerts into Nyria, parsed against your strategy, and routed to a connected broker — using the same webhook endpoint that TradingView and Discord use under the hood.

Who this is for. You already have something that emits signals — a Python script, a Node service, a no-code automation tool, a proprietary scanner — and you want it to place orders through your Nyria-connected broker. If you're using TradingView or Discord instead, use those guides: this page covers the raw HTTP integration.

What you need before you start

  • A Nyria account with a configured strategy (entry/exit rules, position sizing).
  • A connected broker. The walkthrough uses Alpaca but the same payload works for every broker in the catalog.
  • Anything that can send an HTTPS POSTcurl, a backend job, a Zapier/Make webhook step, a custom dashboard.

Steps

Copy your strategy webhook URL

In the Nyria app, open your strategy and go to the Integration tab. Copy the webhook URL. It looks like this:

https://trades.nyria.io/webhooks/wh_8f4c2a91b7d34e0fae9c1b6d2e8f0a4c

The path segment after /webhooks/ is your strategy's token hash (tokenHash). It's the only authentication on the request — treat it like a password. There is no separate Authorization header.

Do not commit the URL to a public repo. The token hash is the full authentication. Anyone who has it can fire alerts at your strategy. If it leaks, regenerate it from the Integration tab — that rotates the token and invalidates the old one immediately.

Send the minimum-viable payload

The smallest valid body is two fields: ticker and action. Open a terminal and run:

curl -X POST https://trades.nyria.io/webhooks/wh_REPLACE_WITH_YOUR_TOKEN \
  -H "Content-Type: application/json" \
  -d '{
    "ticker": "AAPL",
    "action": "buy"
  }'

Replace wh_REPLACE_WITH_YOUR_TOKEN with the token hash you copied in Step 1. Nyria looks up the strategy by token, matches buy against your entry rule, builds the order using the strategy's position-sizing config, and forwards it to the connected broker.

You should see the alert show up in the strategy's Logs tab within a second or two.

Override sizing for a single alert

The strategy controls position sizing by default. To override it for one alert, add quantity (shares/contracts) or notional (USD):

curl -X POST https://trades.nyria.io/webhooks/wh_REPLACE_WITH_YOUR_TOKEN \
  -H "Content-Type: application/json" \
  -d '{
    "ticker": "AAPL",
    "action": "buy",
    "quantity": 25,
    "order_type": "limit",
    "price": 175.50,
    "duration": "day"
  }'
  • quantity — explicit share count for equities, contract count for options.
  • notional — dollar-based sizing (useful for crypto; e.g. "notional": 500).
  • order_typemarket, limit, stop, or stop_limit. Default is market.
  • price — required when order_type is anything other than market.
  • durationday, gtc, pre, or post. Default is day.

Send a plain-text body instead of JSON

JSON is the recommended format, but if your source can only emit free-form text (older alerting platforms, some IFTTT/Zapier steps), Nyria's parser also reads plain text:

curl -X POST https://trades.nyria.io/webhooks/wh_REPLACE_WITH_YOUR_TOKEN \
  -H "Content-Type: text/plain" \
  --data-binary "AAPL long entry"

The parser extracts the instrument (AAPL), the direction (long), and the action (entry). Set Content-Type: text/plain so Nyria doesn't try to JSON-decode the body.

JSON is more deterministic. Plain text is convenient, but the parser has to infer fields. For production setups where every alert matters, send JSON with explicit ticker + action.

Add idempotency for retry-safe sends

If your script retries on network failures (or your provider delivers the same alert twice), Nyria's default behavior protects you: identical payloads received within 60 seconds are deduplicated by hash and only the first one places an order.

If you actually want to scale into the same position — for example, firing the same buy payload twice on purpose — add a unique idempotency_key:

curl -X POST https://trades.nyria.io/webhooks/wh_REPLACE_WITH_YOUR_TOKEN \
  -H "Content-Type: application/json" \
  -d '{
    "ticker": "AAPL",
    "action": "buy",
    "quantity": 10,
    "idempotency_key": "scale-in-2026-06-05-09-31-15"
  }'

Each distinct idempotency_key value is treated as a new alert. Reuse a key and Nyria treats it as a duplicate and skips it.

Recommended formats for the key:

  • A monotonically increasing counter your script tracks (scale-in-1, scale-in-2).
  • An ISO-8601 timestamp with second precision (2026-06-05T09:31:15Z).
  • A UUID v4 minted per alert.

Handle the response

Nyria responds with an HTTP status code and a JSON body. Wire your sender to log or alert on non-2xx responses.

StatusMeaningWhat to do
200 with accepted: trueOrder routed to the broker.Nothing — success.
200 with skipped: trueStrategy is paused, instrument filtered out, or duplicate within the 60-second window.Inspect the response reason field. Usually expected.
401Token hash in URL is invalid or revoked.Re-copy the URL from the Integration tab.
422A field required by your strategy was missing or malformed.The response lists the missing fields. Add them and resend.
429Too many alerts in too short a window.Back off and retry; consider batching upstream.

A minimal Python sender that surfaces failures:

import requests

WEBHOOK_URL = "https://trades.nyria.io/webhooks/wh_REPLACE_WITH_YOUR_TOKEN"

def fire_alert(payload: dict) -> None:
    r = requests.post(WEBHOOK_URL, json=payload, timeout=10)
    r.raise_for_status()
    body = r.json()
    if body.get("skipped"):
        print(f"Nyria skipped: {body.get('reason')}")
    else:
        print(f"Nyria accepted: alert id {body.get('alert_id')}")

fire_alert({"ticker": "AAPL", "action": "buy", "quantity": 10})

raise_for_status() turns 4xx/5xx into exceptions so your retry logic can see them. Always use a finite timeout — Nyria responds quickly, but a hung socket can wedge your producer.

Confirm the order in the broker

Open your broker dashboard (or the Nyria app's bot view) and verify the order landed. If you used the curl from Step 2 against a paper account, you should see an AAPL market buy at the strategy's default size.

If the order is not there, check the strategy's Logs tab. Every inbound webhook generates a log entry — accepted, skipped, or rejected — with the reason and any parse output.

JSON vs plain text — when to use which

Use JSON whenUse plain text when
You control the source (custom script, backend service).Your source only emits free-form strings (legacy alert tool).
You need explicit order_type, price, duration, or option legs.A two-word signal (AAPL long) is enough.
You want the response codes to map cleanly to your retry logic.You're prototyping and just need something to fire.
You're sending option spreads (the legs array requires JSON).

For everything beyond the basics — option spreads, multi-leg orders, every accepted field — see the Webhook Payload Reference.

Idempotency rules at a glance

  • Same payload within 60 seconds → deduplicated automatically. No idempotency_key needed for at-least-once retry safety.
  • Same payload after 60 seconds → treated as a new alert. If you do not want this, add an idempotency_key that you stop reusing after a few minutes.
  • Distinct idempotency_key values → always treated as separate alerts, even if every other field matches. This is the scale-in path.
  • Same idempotency_key reused → second send is skipped with a 200 skipped: true response.

The dedupe window is per strategy. Two different strategies receiving the same payload at the same second are independent — each will fire its own order. If you fan one signal out to multiple strategies, expect multiple orders.

Sample: full crypto buy via curl

A complete crypto example that exercises notional sizing, a limit order, and an idempotency key:

curl -X POST https://trades.nyria.io/webhooks/wh_REPLACE_WITH_YOUR_TOKEN \
  -H "Content-Type: application/json" \
  -d '{
    "ticker": "BTC/USDT",
    "action": "buy",
    "notional": 250,
    "order_type": "limit",
    "price": 65000,
    "duration": "gtc",
    "idempotency_key": "btc-dca-2026-06-05"
  }'

For a spot crypto strategy connected to Binance, this places a $250-notional limit buy at $65,000, good-til-cancelled, and is safe to retry indefinitely as long as the idempotency_key does not change.

Sample: full equity exit via curl

curl -X POST https://trades.nyria.io/webhooks/wh_REPLACE_WITH_YOUR_TOKEN \
  -H "Content-Type: application/json" \
  -d '{
    "ticker": "AAPL",
    "action": "exit",
    "sentiment": "flat"
  }'

sentiment: flat mirrors TradingView's {{strategy.market_position}} post-exit value and tells Nyria the strategy is now out of the market. Use this for full-close alerts from any source.

Troubleshooting

  • Nothing happens after the POST. Check the strategy Logs tab. If there is no row, the request never reached Nyria — verify the URL and that your sender is hitting https:// (not http://).
  • 401 Unauthorized. The token hash is wrong or has been regenerated. Re-copy it from the strategy's Integration tab.
  • 422 with a field list. Your strategy requires a field your payload did not include — for example, an option strategy needs a legs array. The response body lists exactly which fields are missing.
  • Order placed but on the wrong instrument. The parser inferred a different ticker from your text. Switch to JSON and set ticker explicitly.
  • Duplicates skipped when you meant to scale in. Add an idempotency_key that varies per intended order.

For a deeper error-code map, see /troubleshooting.

What's next

  • Webhook Payload Reference — every field Nyria accepts, with examples for every broker.
  • Alpaca Integration — connect the broker used in this tutorial.
  • Alert Sources — TradingView and Discord setups built on the same webhook (Telegram coming soon).
  • Strategies — entry/exit rules and sizing config that this webhook flows into.