curl --request POST \
--url https://testnet.edel-api.xyz/v1/auth/session/renew \
--header 'Authorization: Bearer <token>'import requests
url = "https://testnet.edel-api.xyz/v1/auth/session/renew"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://testnet.edel-api.xyz/v1/auth/session/renew', 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://testnet.edel-api.xyz/v1/auth/session/renew",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$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://testnet.edel-api.xyz/v1/auth/session/renew"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://testnet.edel-api.xyz/v1/auth/session/renew")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://testnet.edel-api.xyz/v1/auth/session/renew")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"version": "external-auth-session-renewal/v1",
"token": "<redacted>",
"expiresAtEpochMs": 1790000900000,
"renewableUntilEpochMs": 1790043200000
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "promo_code_unknown",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"featureFlag": "<string>",
"orderOutcome": "not_submitted",
"clientOrderIds": [
"<string>"
],
"reconciliation": "<string>",
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "custodial_signer_environment_not_service_held",
"withdrawalFailure": "requested",
"feeShareAcceptRefusal": "challenge_missing",
"dependency": "dfns",
"squidRouteRefusal": "route_not_found",
"positionSnapshotRefusal": "position_projection_behind_live",
"setupKeyRefusal": "setup_key_refused"
}{
"version": "external-rest-error/v1",
"requestId": "request-id",
"code": "unauthorized",
"safeMessage": "A verified end-user session is required.",
"retryable": false,
"recoveryActions": [
"do_not_retry"
],
"occurredAtEpochMs": 0
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "promo_code_unknown",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"featureFlag": "<string>",
"orderOutcome": "not_submitted",
"clientOrderIds": [
"<string>"
],
"reconciliation": "<string>",
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "custodial_signer_environment_not_service_held",
"withdrawalFailure": "requested",
"feeShareAcceptRefusal": "challenge_missing",
"dependency": "dfns",
"squidRouteRefusal": "route_not_found",
"positionSnapshotRefusal": "position_projection_behind_live",
"setupKeyRefusal": "setup_key_refused"
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "promo_code_unknown",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"featureFlag": "<string>",
"orderOutcome": "not_submitted",
"clientOrderIds": [
"<string>"
],
"reconciliation": "<string>",
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "custodial_signer_environment_not_service_held",
"withdrawalFailure": "requested",
"feeShareAcceptRefusal": "challenge_missing",
"dependency": "dfns",
"squidRouteRefusal": "route_not_found",
"positionSnapshotRefusal": "position_projection_behind_live",
"setupKeyRefusal": "setup_key_refused"
}{
"version": "external-rest-error/v1",
"requestId": "request-id",
"code": "service_degraded",
"safeMessage": "Authentication dependencies are temporarily unavailable.",
"retryable": true,
"recoveryActions": [
"observe_status",
"retry_after"
],
"occurredAtEpochMs": 0
}Renew the signed-in session before it ends
Called by the signed-in app with its own current session bearer, on the realtime auth_expiring frame or shortly before expiresAtEpochMs. Returns a fresh end-user bearer for the same user and the same accounts, with no change of rights; the app then rotates its realtime ticket with the new bearer and re-authenticates the open socket, which keeps its private channels. Each bearer renews once: a second renewal of the same bearer, also a concurrent one, answers 401, and any bearer minted for a refused or failed renewal is logged out at the provider and never returned. The previous bearer is not extended and ends at its own expiry. A 503 normally leaves the bearer renewable, so the app may retry once with the same bearer; if the store had already committed the renewal when the failure happened (for example the commit succeeded but its reply was lost), that retry answers 401 and the app signs in again. The renewal window is anchored at the first renewal of a sign-in: renewal stops 12 hours after it (renewableUntilEpochMs) and no returned bearer ends later than that, so a whole session lasts at most the sign-in bearer’s own life plus 12 hours; after it the trader signs in again. An API key or a registered test session is refused with 403 passkey_required before the bearer is verified; an expired or revoked bearer and a bot session are refused with 401. The response is never cacheable.
Method and path: POST /v1/auth/session/renew
Authentication: DFNS trader bearer required.
Request schema: none.
Response schema: ExternalAuthSessionRenewalResponse.
Error envelope: ExternalRestError (external-rest-error/v1).
curl --request POST \
--url https://testnet.edel-api.xyz/v1/auth/session/renew \
--header 'Authorization: Bearer <token>'import requests
url = "https://testnet.edel-api.xyz/v1/auth/session/renew"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://testnet.edel-api.xyz/v1/auth/session/renew', 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://testnet.edel-api.xyz/v1/auth/session/renew",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$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://testnet.edel-api.xyz/v1/auth/session/renew"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://testnet.edel-api.xyz/v1/auth/session/renew")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://testnet.edel-api.xyz/v1/auth/session/renew")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"version": "external-auth-session-renewal/v1",
"token": "<redacted>",
"expiresAtEpochMs": 1790000900000,
"renewableUntilEpochMs": 1790043200000
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "promo_code_unknown",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"featureFlag": "<string>",
"orderOutcome": "not_submitted",
"clientOrderIds": [
"<string>"
],
"reconciliation": "<string>",
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "custodial_signer_environment_not_service_held",
"withdrawalFailure": "requested",
"feeShareAcceptRefusal": "challenge_missing",
"dependency": "dfns",
"squidRouteRefusal": "route_not_found",
"positionSnapshotRefusal": "position_projection_behind_live",
"setupKeyRefusal": "setup_key_refused"
}{
"version": "external-rest-error/v1",
"requestId": "request-id",
"code": "unauthorized",
"safeMessage": "A verified end-user session is required.",
"retryable": false,
"recoveryActions": [
"do_not_retry"
],
"occurredAtEpochMs": 0
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "promo_code_unknown",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"featureFlag": "<string>",
"orderOutcome": "not_submitted",
"clientOrderIds": [
"<string>"
],
"reconciliation": "<string>",
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "custodial_signer_environment_not_service_held",
"withdrawalFailure": "requested",
"feeShareAcceptRefusal": "challenge_missing",
"dependency": "dfns",
"squidRouteRefusal": "route_not_found",
"positionSnapshotRefusal": "position_projection_behind_live",
"setupKeyRefusal": "setup_key_refused"
}{
"version": "external-rest-error/v1",
"requestId": "<string>",
"code": "promo_code_unknown",
"safeMessage": "<string>",
"retryable": true,
"recoveryActions": [
"none"
],
"occurredAtEpochMs": 1,
"featureFlag": "<string>",
"orderOutcome": "not_submitted",
"clientOrderIds": [
"<string>"
],
"reconciliation": "<string>",
"realtimeTicketFailure": "invalid",
"apiKeyRejection": "malformed",
"agentRefusal": "agents_not_admitted_in_environment",
"rejectionGuard": "custodial_signer_environment_not_service_held",
"withdrawalFailure": "requested",
"feeShareAcceptRefusal": "challenge_missing",
"dependency": "dfns",
"squidRouteRefusal": "route_not_found",
"positionSnapshotRefusal": "position_projection_behind_live",
"setupKeyRefusal": "setup_key_refused"
}{
"version": "external-rest-error/v1",
"requestId": "request-id",
"code": "service_degraded",
"safeMessage": "Authentication dependencies are temporarily unavailable.",
"retryable": true,
"recoveryActions": [
"observe_status",
"retry_after"
],
"occurredAtEpochMs": 0
}Authorizations
The authorization field takes the session bearer returned by sign-in, or an API key where the route allows keys. Paste the token itself into the authorization field. When invitation-only access is enabled, private operations require the account's sign-in email to be invited. Sign-in can complete without an invitation, but private HTTP, MCP and FIX operations refuse with access_not_allowlisted (HTTP 403): "Private access is limited to invited accounts. Contact support to request access." Public market data remains available. Invitation changes apply to the next private operation, including existing FIX sessions. Do not retry until support confirms access.