Skip to content

Developers

Build on top of your short links

A REST API with scoped keys, signed webhooks and ready-made tools for your browser, WordPress, Slack and Zapier.

Quick start

  1. Create a secret API key on the API keys page of your workspace.
  2. Send it as a bearer token to https://qoslink.cloud/api/v1.
  3. Create your first link:
curl -X POST https://qoslink.cloud/api/v1/links \
  -H "Authorization: Bearer ps_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"destination_url": "https://example.com/spring-sale", "alias": "spring"}'

Scopes

Every key has scopes. A key can never do more than the role of the member who created it.

  • links.read Read links
  • links.write Create, change and delete links
  • qr.read Read QR codes
  • qr.write Create, change and delete QR codes
  • bio.read Read bio pages
  • bio.write Create, change and delete bio pages
  • analytics.read Read statistics and export them
  • analytics.write Reset link statistics
  • conversions.write Record conversions (server side)
  • domains.read Read domains
  • domains.write Add, verify and remove domains
  • webhooks.read Read webhooks and deliveries
  • webhooks.write Manage webhooks and Zapier subscriptions
  • track Browser conversion tracking (publishable keys)
  • billing.read Read the plan, usage, invoices and payments
  • billing.write Start checkouts, cancel or resume the subscription, redeem vouchers
  • members.read Read team members and invitations
  • members.write Invite, change and remove team members
  • audit.read Read the workspace audit log

Errors and rate limits

Errors use one envelope with a stable machine-readable code:

{"error": {"code": "insufficient_scope", "message": "...", "scope": "links.write"}}

Requests per minute depend on your plan; every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Use an Idempotency-Key header on POST requests you may retry.

Webhooks

Events are POSTed as JSON to your endpoint:

{
  "id": "01JC5Z1Q9Y7J1K3M2N4P6R8T0V",
  "type": "link.created",
  "created_at": "2026-11-01T10:00:00.000Z",
  "workspace_id": 42,
  "data": { "id": 123, "alias": "spring", "short_url": "https://sho.rt/spring" }
}

Verifying signatures

Compute HMAC-SHA256 of "<timestamp>.<raw body>" with your signing secret and compare it with v1 in constant time; reject timestamps older than 5 minutes.

Shortener-Signature: t=1793527200,v1=5f2b...e9
Shortener-Event: link.created
Shortener-Delivery: 9812

PHP

$payload = file_get_contents('php://input');
$header = $_SERVER['HTTP_SHORTENER_SIGNATURE'] ?? '';
parse_str(str_replace(',', '&', $header), $parts);
$expected = hash_hmac('sha256', $parts['t'].'.'.$payload, $secret);
$valid = hash_equals($expected, $parts['v1'] ?? '') && abs(time() - (int) $parts['t']) <= 300;

Node.js

const [t, v1] = header.split(',').map((p) => p.split('=')[1]);
const expected = crypto.createHmac('sha256', secret).update(t + '.' + rawBody).digest('hex');
const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)) && Math.abs(Date.now() / 1000 - t) <= 300;

Answer with any 2xx status within 8 seconds. Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2, 6, 12 and 24 hours; after 15 failures in a row the webhook is disabled. Use the event id to ignore duplicates.

Events

Links

  • link.created - Link created
  • link.updated - Link updated
  • link.deleted - Link deleted
  • link.status_changed - Link status changed
  • link.expired - Link expired
  • link.clicked - Link clicks (batched)
  • link.milestone - Click milestone reached
  • link.health_changed - Link health changed
  • ab.winner_promoted - A/B test winner promoted

Analytics

  • conversion.created - Conversion recorded

QR codes

  • qr.created - QR code created
  • qr.updated - QR code updated
  • qr.deleted - QR code deleted
  • qr.scanned - QR scans (batched)
  • qr.batch_completed - Bulk QR job finished

Bio pages

  • bio.published - Bio page published
  • bio.deleted - Bio page deleted
  • bio.form_submitted - Bio form submitted
  • bio.block_clicked - Bio block clicks (batched)

Domains

  • domain.added - Domain added
  • domain.verified - Domain verified
  • domain.verification_failed - Domain verification failed
  • domain.removed - Domain removed

Billing & usage

  • subscription.created - Subscription started
  • subscription.updated - Subscription renewed or changed
  • subscription.canceled - Subscription canceled
  • payment.succeeded - Payment succeeded
  • payment.failed - Payment failed
  • quota.threshold_reached - Usage limit at 80% / 100%

Tools

Browser extension

Shorten the current tab, copy the link and show the QR code (Chrome, Edge, Brave).

Available from the site owner; load it unpacked in chrome://extensions.

Bookmarklet

Drag the button to your bookmarks bar and click it on any page.

Shorten

WordPress

Branded short links for posts and pages, the "Get Shortlink" button and a [shortlink] shortcode.

Download the plugin

Zapier

REST hooks for triggers (subscribe / unsubscribe / samples) and the links API for actions.

POST https://qoslink.cloud/api/v1/hooks   {"target_url": "...", "event": "link.created"}
DELETE https://qoslink.cloud/api/v1/hooks/{id}
GET https://qoslink.cloud/api/v1/hooks/samples/link.created

Slack

Connect your Slack workspace on the Apps & tools page and type /shorten https://example.com.

iPhone & iPad

An Apple Shortcut shortens links from the share sheet.

Instructions