SDK libraries · official and open source

Libraries SDK — ready-made packages for the most popular programming languages.

The official Wthaiq libraries: open source, typed, and designed to make integration a matter of minutes. Install the package with a single command, configure the client with your key, and create your first signature request without writing an HTTP layer by hand. The library takes care of safe retries, cursor pagination, API version pinning, and Webhooks signature verification.

Open source Built-in types (typed) Follows SemVer
$ npm i @wthaiq/node JS Node.js v1.x PY Python v1.x PHP PHP v1.x GO Go v1.x RB Ruby v1.x .NET .NET v1.x Wthaiq-Version: 2026-07-01
Official libraries

Packages Officially supported for each working environment.

All the libraries share the same resource interface and operation names, so what you learn in one language applies to the rest. Node.js, Python and PHP are the tier-1 libraries with complete examples, and Go, Ruby and .NET are available alongside them.

TypeScript ready with complete type definitions, and runs on Node and edge runtimes.

Installation
terminal
npm i @wthaiq/node
Configuration and creating a signature request
signature_request.js
import Wthaiq from '@wthaiq/node';
const wt = new Wthaiq('sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');

const sr = await wt.signatureRequests.create({
  title: 'Employment contract',
  source: { type: 'template', template_id: 'tpl_employment' },
  legal_level: 'aes',
  signers: [{ name: 'Ahmed Mohamed', email: 'ahmed@example.com',
              method: 'draw', require_identity: true }]
});
console.log(sr.id, sr.status); // sr_3n8Kd2Qa1V sent

Type definitions through type hints and stub files, with async support where needed.

Installation
terminal
pip install wthaiq
Configuration and creating a signature request
signature_request.py
import wthaiq
wt = wthaiq.Client('sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')

sr = wt.signature_requests.create(
    title='Employment contract',
    source={'type': 'template', 'template_id': 'tpl_employment'},
    legal_level='aes',
    signers=[{'name': 'Ahmed Mohamed', 'email': 'ahmed@example.com',
              'method': 'draw', 'require_identity': True}],
)
print(sr.id, sr.status)  # sr_3n8Kd2Qa1V sent

PSR-compliant, works with Laravel, Symfony and any Composer project, with strict types.

Installation
terminal
composer require wthaiq/wthaiq-php
Configuration and creating a signature request
signature_request.php
$wt = new \Wthaiq\Client('sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');

$sr = $wt->signatureRequests->create([
  'title' => 'Employment contract',
  'source' => ['type' => 'template', 'template_id' => 'tpl_employment'],
  'legal_level' => 'aes',
  'signers' => [[
    'name' => 'Ahmed Mohamed', 'email' => 'ahmed@example.com',
    'method' => 'draw', 'require_identity' => true,
  ]],
]);
echo $sr->id; // sr_3n8Kd2Qa1V

Additional libraries

— with the same resource interface, available on GitHub under an open-source licence.

Go

pkg.go.dev
v1.x
terminal
go get github.com/wthaiq/wthaiq-go
github.com/wthaiq/wthaiq-go

Ruby

RubyGems · wthaiq
v1.x
terminal
gem install wthaiq
github.com/wthaiq/wthaiq-ruby

.NET

NuGet · Wthaiq
v1.x
terminal
dotnet add package Wthaiq
github.com/wthaiq/wthaiq-dotnet
What every library provides

Consistent behaviour Production ready in every language.

You do not need to rebuild the networking and security logic. Every library implements the following capabilities in the same way, so you get a consistent experience across your projects.

Safe automatic retries

On network errors or transient responses (429/5xx) the library retries with exponential backoff, and automatically attaches an Idempotency-Key with every POST request so that no creation is duplicated.

Automatic cursor-based pagination

Iterate over thousands of records without managing cursors by hand. The iterator fetches the following pages automatically via starting_after depending on next_cursor andhas_more.

Verifying the Webhooks signature

A ready-made helper function (constructEvent) verifies the header Wthaiq-Signature with a constant-time comparison, enforces a 300-second tolerance, and then returns the verified event object.

Typed errors

API errors are translated into typed exceptions that match the error envelope: AuthenticationError andInvalidRequestError andRateLimitError and others, with code andparam andrequest_id.

Pinning the API version

Every library sends the header Wthaiq-Version: 2026-07-01 pinned with every request, so your integrations are unaffected by any later changes. You can override the version per client or per request.

Configurable timeouts

Configure the connect and read timeouts, the number of retries and the HTTP client used (an enterprise proxy, for example) for each client, to suit your environment and your reliability requirements.

Practical examples

Three common tasks with the full code.

Choose your language and copy the example directly: create a signature request, iterate through lists with automatic pagination, and verify the Webhook signature before processing.

Node.js Python PHP
a

Create a signature request

create.js
import Wthaiq from '@wthaiq/node';
import { randomUUID } from 'node:crypto';

const wt = new Wthaiq(process.env.WTHAIQ_API_KEY);

const sr = await wt.signatureRequests.create({
  title: 'Employment contract — Ahmed M.',
  source: { type: 'template', template_id: 'tpl_employment' },
  legal_level: 'aes',
  ordered: true,
  signers: [{
    name: 'Ahmed Mohamed',
    email: 'ahmed@example.com',
    type: 'individual',
    method: 'draw',
    require_identity: true,
    fields: { job_title: 'Software engineer', salary: '25000' }
  }],
  reminders: { enabled: true, interval_hours: 48, max: 3 },
  metadata: { order_id: 'A-1024' }
}, { idempotencyKey: randomUUID() });

console.log(sr.id, sr.status); // sr_3n8Kd2Qa1V sent
b

Iterating over lists (automatic pagination)

list.js
// The iterator fetches the following pages automatically using the cursor
for await (const sr of wt.signatureRequests.list({ status: 'completed', limit: 100 })) {
  console.log(sr.id, sr.reference);
}
c

Verifying the Webhook signature

webhook.js
import express from 'express';
const app = express();

// Pass the raw body for signature verification
app.post('/hooks/wthaiq', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['wthaiq-signature'];
  let event;
  try {
    event = wt.webhooks.constructEvent(req.body, sig, process.env.WTHAIQ_WEBHOOK_SECRET);
  } catch (err) {
    return res.status(400).send(`signature check failed: ${err.message}`);
  }
  if (event.type === 'signature_request.completed') {
    const sr = event.data.object; // signature_request object
    // Activate the account or store the signed document (run the heavy work later)
  }
  res.json({ received: true });
});
a

Create a signature request

create.py
import os, uuid, wthaiq

wt = wthaiq.Client(os.environ['WTHAIQ_API_KEY'])

sr = wt.signature_requests.create(
    title='Employment contract — Ahmed M.',
    source={'type': 'template', 'template_id': 'tpl_employment'},
    legal_level='aes',
    ordered=True,
    signers=[{
        'name': 'Ahmed Mohamed',
        'email': 'ahmed@example.com',
        'type': 'individual',
        'method': 'draw',
        'require_identity': True,
        'fields': {'job_title': 'Software engineer', 'salary': '25000'},
    }],
    reminders={'enabled': True, 'interval_hours': 48, 'max': 3},
    metadata={'order_id': 'A-1024'},
    idempotency_key=str(uuid.uuid4()),
)

print(sr.id, sr.status)  # sr_3n8Kd2Qa1V sent
b

Iterating over lists (automatic pagination)

list.py
# auto_paging_iter walks through every page automatically
for sr in wt.signature_requests.list(status='completed', limit=100).auto_paging_iter():
    print(sr.id, sr.reference)
c

Verifying the Webhook signature

webhook.py
import os, wthaiq
from flask import Flask, request

app = Flask(__name__)
endpoint_secret = os.environ['WTHAIQ_WEBHOOK_SECRET']

@app.post('/hooks/wthaiq')
def handle():
    payload = request.get_data()
    sig = request.headers.get('Wthaiq-Signature')
    try:
        event = wthaiq.Webhook.construct_event(payload, sig, endpoint_secret)
    except wthaiq.error.SignatureVerificationError:
        return 'invalid signature', 400
    if event.type == 'signature_request.completed':
        sr = event.data.object  # signature_request object
        # Activate the account or store the signed document
    return {'received': True}
a

Create a signature request

create.php
require 'vendor/autoload.php';

$wt = new \Wthaiq\Client(getenv('WTHAIQ_API_KEY'));

$sr = $wt->signatureRequests->create([
  'title' => 'Employment contract — Ahmed M.',
  'source' => ['type' => 'template', 'template_id' => 'tpl_employment'],
  'legal_level' => 'aes',
  'ordered' => true,
  'signers' => [[
    'name' => 'Ahmed Mohamed',
    'email' => 'ahmed@example.com',
    'type' => 'individual',
    'method' => 'draw',
    'require_identity' => true,
    'fields' => ['job_title' => 'Software engineer', 'salary' => '25000'],
  ]],
  'reminders' => ['enabled' => true, 'interval_hours' => 48, 'max' => 3],
  'metadata' => ['order_id' => 'A-1024'],
], ['idempotency_key' => bin2hex(random_bytes(16))]);

echo $sr->id . ' ' . $sr->status; // sr_3n8Kd2Qa1V sent
b

Iterating over lists (automatic pagination)

list.php
// autoPagingIterator fetches the following pages automatically using the cursor
foreach ($wt->signatureRequests->all(['status' => 'completed', 'limit' => 100]) as $sr) {
    echo $sr->id . ' ' . $sr->reference . "\n";
}
c

Verifying the Webhook signature

webhook.php
require 'vendor/autoload.php';

$payload = file_get_contents('php://input');
$sig     = $_SERVER['HTTP_WTHAIQ_SIGNATURE'] ?? '';
$secret  = getenv('WTHAIQ_WEBHOOK_SECRET');

try {
    $event = \Wthaiq\Webhook::constructEvent($payload, $sig, $secret);
} catch (\Wthaiq\Exception\SignatureVerificationException $e) {
    http_response_code(400);
    exit('invalid signature');
}

if ($event->type === 'signature_request.completed') {
    $sr = $event->data->object; // signature_request object
    // Activate the account or store the signed document
}
http_response_code(200);
Note: To verify the Webhook signature you must pass the raw body exactly as it arrived, before any JSON parsing. The helper function relies on the header Wthaiq-Signature: t=...,v1=... and the secret whsec_..., compares it with a constant-time comparison and rejects any request whose time difference exceeds 300 seconds.
Versions and support

Policy Clear and stable to upgrade.

We commit to predictable limits on change so that you can plan your upgrades with confidence, and we separate the library version from the API version.

SemVer versioning

Every library follows semantic versioning MAJOR.MINOR.PATCH. Breaking changes are only introduced in a new MAJOR version; compatible additions and bug fixes ship safely in MINOR and PATCH releases.

The library version is independent of the API version pinned through Wthaiq-Version, so you can upgrade the library without changing API behaviour.

Deprecation policy

API changes are dated through the version header, and any released version stays supported. When an old capability is deprecated we announce it in the changelog and allow a transition period of at least 12 months before removal.

Deprecation warnings are also emitted through response headers and library logs, so you know early what needs updating. See Changelog regularly.

Minimum runtime versions

LanguageMinimum runtime requirementsPackage sourceVersion
Node.jsNode.js 18+npm · @wthaiq/nodev1.x
PythonPython 3.8+PyPI · wthaiqv1.x
PHPPHP 8.1+Packagist · wthaiq/wthaiq-phpv1.x
GoGo 1.21+pkg.go.dev · wthaiq/wthaiq-gov1.x
RubyRuby 3.0+RubyGems · wthaiqv1.x
.NET.NET 6.0+NuGet · Wthaiqv1.x
The shared foundation: All libraries pin Wthaiq-Version: 2026-07-01 by default, and connects tohttps://wthaiq.com/api/v1, and authenticate through Authorization: Bearer sk_.... There is no test mode — every sk_ live as soon as it is created; the publishable key pk_... for safe use in the browser, and remains limited to specific paths and domains.
FAQs

Developer questions About the libraries.

Which languages are officially supported?

We publish six official libraries: Node.js, Python and PHP as tier-1 libraries with complete examples and broader support, plus Go, Ruby and .NET. All of them share the same resource interface and operation names, so what you learn in one language applies directly to the rest.

Are the libraries open source?

Yes, all the libraries are open source and published under the organisation github.com/wthaiq, and you can follow the code, open issues and contribute. The packages are distributed through their standard registries: npm, PyPI, Packagist, pkg.go.dev, RubyGems and NuGet.

How does the library handle retries and safe repetition?

The library retries automatically on network errors and transient responses (429 and 5xx) with exponential backoff, and attaches to every POST request the key Idempotency-Key unique. Because the key is stored on the server for 24 hours, no retry creates a duplicate resource. You can also pass your own key with each request.

How do I pin the API version inside the library?

Every library sends the header Wthaiq-Version: 2026-07-01 pinned by default with every request, so API responses keep the same shape despite any later updates. You can override the version when configuring the client, or per request, whenever you want to adopt a newer version after testing it.

What are the minimum runtime versions and the deprecation policy?

Minimum: Node.js 18, Python 3.8, PHP 8.1, Go 1.21, Ruby 3.0 and .NET 6.0. The libraries follow SemVer, so there are no breaking changes except in a new major release. When a capability is discontinued we announce it in the changelog and allow a transition period of at least 12 months before removal.

Install the library and sign your first document today.

Start with the library for your language and follow the quickstart guide to create your first signature request in minutes — with built-in types and full legal standing.