Zixso for Developers

Get started

Introduction

The Zixso API provides secure, scalable access to link creation, customization, and analytics — built for developers integrating with internal tools, marketing workflows, or customer-facing apps. Each API request is scoped to an organization (called a link space in Zixso). Using service accounts tied to your link space, you can programmatically manage short links with support for custom slugs, metadata, tags, and real-time click analytics — all with minimal overhead.


Terminology

In the Zixso dashboard and user interface, we refer to your account workspace as a link space. In the API, this same concept is represented as an organization (org_id). Throughout this documentation:

  • Link space = your workspace in the Zixso dashboard
  • Organization (org_id) = the same workspace when accessed via API
  • These terms are interchangeable and refer to the same entity

API Domain

https://api.zixso.com/

This is the root domain for all Zixso API requests. All endpoints must be prefixed with a supported version path.


API Versioning

Overview

Current Version: v0.0.1

Zixso uses URI-based versioning, where each API version is specified in the path (e.g., /v0.0.1/). This ensures that your integration remains stable and unaffected by future changes.


Versioning Policy

  • Stable versions are backward-compatible within the same major version (v1.x.x).
  • Breaking changes will only occur in a new major version (e.g., v2.0.0).
  • Older versions will remain available and supported until officially deprecated. Deprecation timelines will be communicated in the changelog.
  • Zixso follows Semantic Versioning: v{MAJOR}.{MINOR}.{PATCH}.

Example endpoint:

GET https://api.zixso.com/v0.0.1/orgs/{org_id}/links

Service Accounts & Credentials

Overview

To use the Zixso API, you need to authenticate every request using a Service Account — a secure identity scoped to your link space (organization).

Each service account can have one or more credentials, which are used to sign or authorize API calls.


Step 1: Create a Service Account

  1. Go to Settings → Service Accounts in the Zixso dashboard.
  2. Click + Create Account.
  3. Provide a label (e.g., Link Automation Bot).
  4. Select a role (e.g., Super Administrator or a custom role).
  5. Click Create.

Note: This service account will be scoped to your current link space, which corresponds to the org_id in API calls.


Step 2: Generate Credentials

  1. Open the service account you created.
  2. Click + Add Credential.
  3. Choose an Authentication Type:
    • API Key – simple and fast
    • HMAC – signed requests with timestamps
    • Public/Private Key – cryptographic, ideal for partners
  4. Set a rotation period (e.g., 90 or 180 days).
  5. Click Create.

The secret key will only be shown once — copy and store it securely.


Authentication

Overview

Zixso supports multiple authentication methods, each using your service account credentials. Choose the method that best fits your use case.


Choosing the Right Method

Method Use Case
API Key Internal tools, backend automation
HMAC Signature Webhooks, secure API integrations
Public Key Federated, external, or zero-trust flows

API Key Authentication

Use Case: Internal automation or testing environments

Header Format

Authorization: APIKey <credential_id>.<token>

Example (Python):

headers = {
    "Authorization": "APIKey f1e87dd7-...<truncated>...",
    "Content-Type": "application/json"
}

HMAC Signature Authentication

Use Case: Server-to-server APIs or signed webhooks

Header Format

Authorization: HMAC key="<credential_id>", timestamp="<unix_timestamp>", 
signature="<base64_signature>"

Message Format (for signing)

<credential_id>\n<timestamp>\n<HTTP_METHOD>\n<PATH>\n<BODY>

Example (Python):

import time, hmac, hashlib, base64

key_id = "a83e6f48-..."
secret = "your_hmac_secret"
timestamp = str(int(time.time()))
method = "POST"
path = "/v0.0.1/orgs/{org_id}/links"
body = '{"destination_url":"https://example.com"}'

message = f"{key_id}\n{timestamp}\n{method}\n{path}\n{body}"
signature = base64.b64encode(hmac.new(secret.encode(), message.encode(), hashlib.sha256).digest()).decode()

headers = {
    "Authorization": f'HMAC key="{key_id}", timestamp="{timestamp}", signature="{signature}"',
    "Content-Type": "application/json"
}

Public/Private Key Authentication

Zixso supports asymmetric cryptographic authentication using a private-public key pair. This method is recommended for high-trust integrations, federated services, and zero-trust environments.


Header Format

Authorization: PublicKey key="{credential_id}", timestamp="{unix_ts}", 
algorithm="rsa4096", signature="{base64_signature}"
  • key: UUID of the public key credential (e.g., a83e6f48-...)
  • timestamp: Unix timestamp in seconds (±5 minutes drift allowed)
  • algorithm: Only rsa4096 is supported (as per current backend)
  • signature: Base64-encoded digital signature created from request body and metadata

Message Format for Signature

The following string is constructed exactly as-is and signed with your private RSA key:

<credential_id>\n<timestamp>\n<HTTP_METHOD>\n<PATH>\n<BODY>
  • No extra whitespace
  • BODY must match the actual POST/PUT/PATCH payload (empty string for GET/DELETE)
  • PATH must be relative (e.g., /v0.0.1/orgs/abc123/links)

Python Example (Using cryptography)

import base64
import time
import requests
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.backends import default_backend

# Inputs
credential_id = "f8fcdc8f-db61-4bbb-94b5-4e7d65aae382"
private_key_path = "./private.pem"
timestamp = str(int(time.time()))
method = "POST"
path = "/v0.0.1/orgs/abc123/links"
body = '{"destination_url":"https://example.com"}'

# Build message
message = f"{credential_id}\n{timestamp}\n{method}\n{path}\n{body}".encode("utf-8")

# Load private key
with open(private_key_path, "rb") as key_file:
    private_key = serialization.load_pem_private_key(key_file.read(), password=None)

# Sign
signature = private_key.sign(
    message,
    padding.PKCS1v15(),
    hashes.SHA256()
)
signature_b64 = base64.b64encode(signature).decode()

# Headers
headers = {
    "Authorization": (
        f'PublicKey key="{credential_id}", '
        f'timestamp="{timestamp}", '
        f'algorithm="rsa4096", '
        f'signature="{signature_b64}"'
    ),
    "Content-Type": "application/json"
}

# Make request
response = requests.post("https://api.zixso.com" + path, data=body, headers=headers)
print(response.status_code, response.text)

Notes & Tips

  • The timestamp must be within ±5 minutes of server time (300s skew allowed)
  • signature must be base64-encoded
  • The algorithm must be rsa4096 — other values (e.g., rsa2048, ecdsa) will be rejected
  • Make sure the body string is exactly what will be sent (including spaces and formatting)

API Basics

Overview

Zixso APIs follow standard REST principles with predictable resource-oriented URLs, structured JSON request/response bodies, and conventional HTTP status codes.

All APIs are versioned and accept authenticated requests scoped to a specific organization (link space).


Authentication

All requests must include a valid Authorization header using one of the supported credential types (API Key, HMAC, or Public Key).


Base URL

All API requests are made to:

https://api.zixso.com/v0.0.1/

Always include the correct version prefix (/v0.0.1/) in your path.


Success Response Format

Most successful responses return a 2xx status code and a structured JSON body:

{
  "id": "2aaf13fc-22a7-4cbb-a7f2-c1be3d9a0dd4",
  "url": "https://example.com"
}

List views follow a consistent pagination structure:

{
  "meta": {
    "pagination": {
      "page": 1,
      "page_size": 10,
      "no_of_records": 53
    }
  },
  "results": [
    { ... }, { ... }
  ]
}

Error Response Format

All error responses follow a consistent structure:

{
  "field_name": [
    "This field is required."
  ]
}

Other examples:

{
  "detail": "Authentication credentials were not provided."
}

Content-Type

All requests and responses use JSON:

Content-Type: application/json
Accept: application/json

HTTP Methods

Method Description
GET Retrieve resources
POST Create new resources
PATCH Update partial fields
PUT Replace entire object
DELETE Delete a resource

Pagination

Paginated endpoints accept:

  • ?page=1
  • ?page_size=25

Default page_size is 10, and max allowed is 100.


Filtering & Query Parameters

Some endpoints support filtering via query parameters (e.g. ?tag=campaign, ?created_after=2025-01-01). Specific filters are documented under each endpoint.


Overview

The Links API lets you create and manage short links with advanced control — including custom aliases, expiration, password protection, redirection rules, cloaking, and tagging. All operations are scoped to your link space (org_id).


POST /orgs/{org_id}/links

Creates a new short link with optional customization. You can provide a custom alias, set expiration, enable cloaking, add password protection, and define redirection rules.

Request Headers

Authorization: APIKey <credential_id>.<token>
Content-Type: application/json

Request Body

{
  "url": "https://www.google.com",
  "alias": "link-to-google",
  "label": "Google Redirect",
  "tags": ["campaign", "summer"],
  "password": "secret123",
  "expiration_time": "2025-12-31T23:59:59Z",
  "cloak": true,
  "redirection_rules": [
    {
      "match": "AND",
      "conditions": [
        { "field": "COUNTRY", "operator": "EQUAL", "value": "IN" }
      ],
      "url": "https://www.google.co.in"
    }
  ]
}

Response

{
    "id": "4c272584-a2ad-4f1c-9996-6fc03de89d1c",
    "url": "https://www.google.com",
    "label": "Google Redirect",
    "domain": null,
    "domain_name": "127.0.0.1:8080",
    "alias": "link-to-google",
    "is_password_protected": true,
    "expiration_time": "2025-12-31T23:59:59Z",
    "cloak": true,
    "redirection_rules": [
        {
            "match": "AND",
            "conditions": [
                {
                    "field": "COUNTRY",
                    "operator": "EQUAL",
                    "value": "IN"
                }
            ],
            "url": "https://www.google.co.in"
        }
    ],
    "tags": [
        "campaign",
        "summer"
    ],
    "created_at": "2025-07-12T12:41:52.174812Z"
}

Key Notes

  • If password is provided, is_password_protected will be true — the actual password is never returned.
  • domain is optional; if omitted, the default domain is used.
  • tags must follow the lowercase-alphanumeric-hyphen format (^[a-z0-9-]{1,30}$) and be unique.
  • redirection_rules must follow strict structure and include either a url or deeplink, not both.

GET /orgs/{org_id}/links

Retrieve all links under your link space.

Query Parameters

  • page=1 (default: 1)
  • page_size=25 (max: 100)
  • q=keyword (searches alias, domain, and back-half)

Example Response

{
    "meta": {
        "pagination": {
            "page": 1,
            "page_size": 10,
            "no_of_records": 1
        }
    },
    "results": [{
        "id": "4c272584-a2ad-4f1c-9996-6fc03de89d1c",
        "url": "https://www.google.com",
        "label": "Homepage Redirect",
        "domain": null,
        "domain_name": "127.0.0.1:8080",
        "alias": "link-to-google",
        "is_password_protected": true,
        "expiration_time": "2025-12-31T23:59:59Z",
        "cloak": true,
        "redirection_rules": [{
            "url": "https://www.google.co.in",
            "match": "AND",
            "conditions": [{
                "field": "COUNTRY",
                "value": "IN",
                "operator": "EQUAL"
            }]
        }],
        "tags": [
            "campaign",
            "summer"
        ],
        "created_at": "2025-07-12T12:41:52.174812Z"
    }]
}

GET /orgs/{org_id}/links/{link_id}

Retrieve full metadata, click count, tags, and rules for a specific link.


PATCH /orgs/{org_id}/links/{link_id}

Update one or more fields of an existing link.

Request Body

{
  "url": "https://www.google.co.in",
}

Only supplied fields will be updated.


DELETE /orgs/{org_id}/links/{link_id}

Deletes a link.

Returns: 204 No Content on success.

Advanced Options

Password Protection

Links can be protected with a password (min 6 characters). End-users must enter the password before redirection. Passwords are securely hashed and never stored in plain text.


Expiration

Set expires_at (ISO8601 format) to automatically deactivate a link after a specific date/time.


Cloaking

Set "cloak": true to hide the final destination from browser address bars. This is useful for embedded or promotional flows.


Redirection Rules

Control how and where users are redirected based on country.

Supported Fields

  • country: ISO 3166-1 alpha-2 codes (e.g. US, AE)
  • os: Android, iOS, macOS/iPadOS, Windows, Linux

Supported Operators

  • EQUAL, NOT_EQUAL, IN, NOT_IN

Redirection Types

  • url: traditional web URL
  • deeplink: opens mobile app or falls back to install page

Example Rule

{
  "match": "AND",
  "conditions": [
    { "field": "country", "operator": "EQUAL", "value": "US" },
    { "field": "os", "operator": "IN", "value": ["iOS", "Android"] }
  ],
  "deeplink": {
    "app_link": "myapp://promo",
    "install_url": "https://example.com/download"
  }
}