Skip to content
API status
Webhooks

Webhooks

Receive signed position lifecycle notifications and process retries safely.

Use webhooks to start and stop event-driven workflows in your organization’s backend. Your application owns its trading rules and decides what to do with each event.

Event When it is sent
position.opened A market or pending order creates an open position.
position.closed A position is fully closed.
position.reduced A position is partially closed, including the remaining volume and closed volume.
finance.transaction.completed A deposit or withdrawal is committed to the account ledger.
account.metrics.updated An account metric batch is accepted for a subscribed account.

Register an HTTPS receiver in Koneth Console → Webhooks, select the events, and store the signing secret securely. Follow Verify webhook signatures.

A new endpoint is created pending and receives no events until it answers Koneth’s verification challenge. In Console → Webhooks, click Verify on the endpoint row. Koneth then POSTs this body to your URL:

{
"type": "koneth.endpoint.verify",
"challenge": "9f2c4d1e7a8b3c5d6e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d"
}

Your receiver must reply with a 2xx status and a JSON body that echoes the same value:

{ "challenge": "9f2c4d1e7a8b3c5d6e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d" }

The challenge is 64 hexadecimal characters, and a new one is issued on every attempt.

Response Result
2xx with the matching challenge The endpoint becomes active and starts receiving deliveries.
Any non-2xx status Verification fails with the status Koneth received.
2xx without a matching challenge Verification fails with “Endpoint challenge did not match”.

The verification request is not signed — it carries no X-Koneth-Signature header — because the signing secret is not in use yet. Verify only that the challenge matches the value in the body. Koneth waits up to eight seconds for the response.

An endpoint that is never verified stays pending and is skipped by delivery, so a receiver that returns 200 for everything but ignores the body will never activate.

Applies to on the endpoint controls its scope:

Selection What it receives
All servers (default) Events from every server in the organization.
A single server Events from that server only.

The scope applies until the endpoint is changed. Every delivery still names its source server in data.serverId, so an organization-wide endpoint can route on that field. If a server is deleted, its server-scoped endpoints are disabled instead of silently widening to the organization.

Edit on an endpoint row changes its URL, the events it listens for, or its scope. Changing the events or the scope takes effect immediately and does not interrupt delivery.

Moving the URL is different. The verification on file was answered by the address that was there before, so a moved endpoint goes back to pending with a fresh challenge and receives nothing until it is verified again. The API reports this as verificationRequired on the update response, and the dialog warns before you save.

Delete removes the endpoint and the log of what was delivered to it, and deliveries stop at once. It cannot be undone. If the receiver is only being rebuilt, disable the endpoint instead and enable it when it is ready.

{
"version": 1,
"id": "d6b3ef8d-95a8-462d-90a5-3ef8fc3a6533",
"type": "position.opened",
"occurredAt": "2026-08-30T14:25:39.804Z",
"data": {
"serverId": "srv_123",
"accountNumber": "100042",
"positionId": "pos_456",
"symbol": "EURUSD",
"side": "BUY",
"volume": 1,
"executionPrice": 1.16542
}
}

Every payload carries version, a stable id, the type, the source occurredAt, and data with serverId and accountNumber. The rest of data depends on the event:

Event Additional data fields
position.opened orderId, positionId, dealId, clientOrderId, status, executionPrice, commission, balance, executionVersion, executedAt
position.closed positionId, symbol, side, closedVolume, remainingVolume, status, executionPrice, realizedPnl, commission, reason, closedAt
position.reduced Same fields as position.closed, with the still-open remainingVolume and a null closedAt
finance.transaction.completed referenceId, type (deposit or withdrawal, with amount signed accordingly), amount, balanceAfter
account.metrics.updated eventId, sequence, measuredAt, balance, equity, usedMargin, freeMargin, marginLevel, unrealizedProfit, dailyDrawdown, totalDrawdown, accountStatus

occurredAt is the source event time. Trade, deal, and transaction records expose their own timestamps through the paginated account endpoints. The delivery timestamp header records the HTTP attempt and is not the trade time.

Verify the signature over the raw body before parsing it. Store id with a uniqueness constraint, return 2xx after durable acceptance, and run business rules asynchronously. Retries are at least once and can arrive out of order.

Delivery attempts use the configured webhook.delivery credit price. A failed receiver, exhausted credits, or a network problem can delay delivery, so silence is not proof that an account has no activity.

  1. Receive position.opened and subscribe that account to Live account metrics.
  2. Evaluate every consecutive live valuation using your organization’s rules.
  3. Receive position.closed and query positions with status=open&limit=1.
  4. Unsubscribe when pagination.total is 0 and the account no longer needs live monitoring.

This keeps lifecycle notifications separate from continuous valuations and avoids polling the Management API for every price change.