Skip to main content

Register Origin

Register an allowed origin for your application. This is required before creating authentication challenges and provides phishing resistance by ensuring only registered origins can initiate authentication.

Endpoint

POST /api/v1/origins/register

Request Body

{
"origin": "https://app.example.com",
"display_name": "Example Application"
}

Request Fields

FieldTypeRequiredDescription
originstringYesFull origin URL (must be HTTPS, except localhost)
display_namestringYesHuman-readable name shown on mobile device

Origin Format

  • Must include scheme: https://
  • No trailing slash
  • Port optional (443 is default for HTTPS)
  • http://localhost allowed for development

Valid examples:

  • https://app.example.com
  • https://dashboard.example.com:8443
  • http://localhost:3000 (development only)

Invalid examples:

  • app.example.com (missing scheme)
  • https://app.example.com/ (trailing slash)
  • http://app.example.com (HTTP not allowed in production)

Response

{
"success": true,
"origin_hash": "a1b2c3d4e5f6..."
}

Response Fields

FieldTypeDescription
successbooleanRegistration result
origin_hashstringSHA256 hash of normalized origin

Errors

Error CodeHTTP StatusDescription
INVALID_REQUEST400Missing origin or display_name
INVALID_ORIGIN400Origin must use HTTPS
DB_ERROR500Database operation failed

Examples

cURL

#!/bin/bash

CLIENT_ID="your-client-id"
CLIENT_SECRET="your-client-secret"
API_BASE="https://ztbxbas01.corezt.com"

TIMESTAMP=$(date +%s)
NONCE=$(openssl rand -hex 32)
BODY='{"origin":"https://app.example.com","display_name":"Example Application"}'

# Generate signature
SIGN_DATA="POST|/api/v1/origins/register|${TIMESTAMP}|${NONCE}|${BODY}"
SIGNATURE=$(echo -n "$SIGN_DATA" | openssl dgst -sha256 -hmac "$CLIENT_SECRET" | awk '{print $2}')

curl -X POST "${API_BASE}/api/v1/origins/register" \
-H "Content-Type: application/json" \
-H "X-Client-ID: ${CLIENT_ID}" \
-H "X-Timestamp: ${TIMESTAMP}" \
-H "X-Nonce: ${NONCE}" \
-H "X-Signature: ${SIGNATURE}" \
-d "$BODY"

Python

import hmac
import hashlib
import time
import secrets
import requests

class ZTXClient:
def __init__(self, client_id: str, client_secret: str, base_url: str = "https://ztxbas01.corezt.com"):
self.client_id = client_id
self.client_secret = client_secret
self.base_url = base_url

def _sign_request(self, method: str, endpoint: str, body: str) -> dict:
timestamp = str(int(time.time()))
nonce = secrets.token_hex(32)

sign_data = f"{method}|{endpoint}|{timestamp}|{nonce}|{body}"
signature = hmac.new(
self.client_secret.encode(),
sign_data.encode(),
hashlib.sha256
).hexdigest()

return {
"X-Client-ID": self.client_id,
"X-Timestamp": timestamp,
"X-Nonce": nonce,
"X-Signature": signature,
"Content-Type": "application/json"
}

def register_origin(self, origin: str, display_name: str) -> dict:
endpoint = "/api/v1/origins/register"
body = f'{{"origin":"{origin}","display_name":"{display_name}"}}'

headers = self._sign_request("POST", endpoint, body)
response = requests.post(f"{self.base_url}{endpoint}", headers=headers, data=body)

return response.json()


# Usage
client = ZTXClient("your-client-id", "your-client-secret")

result = client.register_origin(
origin="https://app.example.com",
display_name="Example Application"
)

print(f"Origin hash: {result['origin_hash']}")

Node.js

const crypto = require('crypto');
const https = require('https');

class ZTXClient {
constructor(clientId, clientSecret, baseUrl = 'https://ztxbas01.corezt.com') {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.baseUrl = new URL(baseUrl);
}

_signRequest(method, endpoint, body) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomBytes(32).toString('hex');

const signData = `${method}|${endpoint}|${timestamp}|${nonce}|${body}`;
const signature = crypto
.createHmac('sha256', this.clientSecret)
.update(signData)
.digest('hex');

return {
'X-Client-ID': this.clientId,
'X-Timestamp': timestamp,
'X-Nonce': nonce,
'X-Signature': signature,
'Content-Type': 'application/json'
};
}

async registerOrigin(origin, displayName) {
const endpoint = '/api/v1/origins/register';
const body = JSON.stringify({ origin, display_name: displayName });
const headers = this._signRequest('POST', endpoint, body);

return new Promise((resolve, reject) => {
const options = {
hostname: this.baseUrl.hostname,
port: this.baseUrl.port || 443,
path: endpoint,
method: 'POST',
headers
};

const req = https.request(options, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => resolve(JSON.parse(data)));
});

req.on('error', reject);
req.write(body);
req.end();
});
}
}

// Usage
const client = new ZTXClient('your-client-id', 'your-client-secret');

client.registerOrigin('https://app.example.com', 'Example Application')
.then(result => {
console.log(`Origin hash: ${result.origin_hash}`);
})
.catch(console.error);

C# (.NET)

using System;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

public class ZTXClient
{
private readonly string _clientId;
private readonly string _clientSecret;
private readonly HttpClient _httpClient;

public ZTXClient(string clientId, string clientSecret, string baseUrl = "https://ztxbas01.corezt.com")
{
_clientId = clientId;
_clientSecret = clientSecret;
_httpClient = new HttpClient { BaseAddress = new Uri(baseUrl) };
}

private Dictionary<string, string> SignRequest(string method, string endpoint, string body)
{
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
var nonce = Convert.ToHexString(RandomNumberGenerator.GetBytes(32)).ToLower();

var signData = $"{method}|{endpoint}|{timestamp}|{nonce}|{body}";

using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(_clientSecret));
var signature = Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(signData))).ToLower();

return new Dictionary<string, string>
{
["X-Client-ID"] = _clientId,
["X-Timestamp"] = timestamp,
["X-Nonce"] = nonce,
["X-Signature"] = signature
};
}

public async Task<OriginRegistrationResponse> RegisterOriginAsync(string origin, string displayName)
{
var endpoint = "/api/v1/origins/register";
var body = JsonSerializer.Serialize(new { origin, display_name = displayName });
var headers = SignRequest("POST", endpoint, body);

var request = new HttpRequestMessage(HttpMethod.Post, endpoint)
{
Content = new StringContent(body, Encoding.UTF8, "application/json")
};

foreach (var header in headers)
{
request.Headers.Add(header.Key, header.Value);
}

var response = await _httpClient.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();

return JsonSerializer.Deserialize<OriginRegistrationResponse>(json);
}
}

public class OriginRegistrationResponse
{
public bool Success { get; set; }
public string OriginHash { get; set; }
}

// Usage
var client = new ZTXClient("your-client-id", "your-client-secret");

var result = await client.RegisterOriginAsync(
"https://app.example.com",
"Example Application"
);

Console.WriteLine($"Origin hash: {result.OriginHash}");

Best Practices

  1. Register during onboarding: Register all your origins when setting up your ZTX integration
  2. Use descriptive display names: Users see this on their mobile device
  3. Register all environments: Development, staging, and production origins
  4. HTTPS only: Never use HTTP in production (blocks registration)