Skip to content

Developers

The Forkly REST API

Create smart links from your backend, CMS or CI pipeline, pull analytics into your warehouse and render QR codes on demand. JSON in, JSON out.

Overview

The API is organised around REST resources, uses standard HTTP verbs and status codes, and always returns JSON (except image endpoints). All requests must use HTTPS.

Base URL
https://1link.1workspace.in/api/v1
Format
JSON request bodies with Content-Type: application/json
Pagination
page and pageSize query parameters; list responses include total and totalPages
Timestamps
ISO 8601 strings in UTC

Authentication

Authenticate every request with an API key sent as a bearer token. Keys start with fk_live_ and belong to a single workspace, so you never need to pass a workspace ID.

  1. Open Dashboard → API keys. API access is included on the Business plan.
  2. Name the key, choose its scopes and, optionally, an expiry.
  3. Copy the secret immediately — it is shown only once. You can regenerate or revoke a key at any time.
bashAuthenticated request
curl https://1link.1workspace.in/api/v1/links \
  -H "Authorization: Bearer fk_live_your_api_key"

Treat keys like passwords: keep them server-side, never embed them in mobile apps or browser code, and rotate them if they may have leaked.

Scopes

Each key is limited to the scopes you grant. Requests outside a key’s scopes fail with 403.

  • links:read

    List links and read a single link.

  • links:write

    Create, update and delete links.

  • analytics:read

    Read summaries, timeseries and breakdowns.

  • qr:read

    Render QR code images for links.

  • domains:read

    List the domains available to the workspace.

Analytics

All analytics endpoints share the same filters.

Shared analytics query parameters
ParameterTypeDescription
rangestringOne of today, yesterday, 7d, 30d (default), 90d, custom.
from, toYYYY-MM-DDRequired when range=custom. Ranges are clipped to your plan’s analytics history.
linkIdstringLimit results to a single link.
sourcestringall (default), link for clicks only or qr for QR scans only.
GET/api/v1/analyticsanalytics:read

Summary

Totals for clicks, uniqueVisitors and qrScans, each with a change versus the previous period, plus the resolved range.

GET/api/v1/analytics/timeseriesanalytics:read

Timeseries

Points of { t, clicks, qrScans, uniqueVisitors } by hour or day depending on the range.

GET/api/v1/analytics/breakdownanalytics:read

Breakdown

Breakdown query parameters
ParameterTypeDescription
dimensionrequiredstringcountry · city · device · os · browser · referrer · platform · utm_source · utm_medium · utm_campaign
limitintegerRows to return, 1–250 (default 10).

Returns rows of { value, label, clicks, share }. City and UTM dimensions require Pro or Business.

QR codes

GET/api/v1/links/:id/qr/imageqr:read

Render a QR image

QR image query parameters
ParameterTypeDescription
formatstringpng or svg.
sizeintegerWidth in pixels, 128–4096.
download1Adds a Content-Disposition header for file downloads.

The image uses the link’s saved QR design (colours, shapes and logo).

bashDownload a 1024px PNG
curl -o summer.png \
  "https://1link.1workspace.in/api/v1/links/LINK_ID/qr/image?format=png&size=1024" \
  -H "Authorization: Bearer fk_live_your_api_key"

Domains

GET/api/v1/domainsdomains:read

List domains

Returns the shared platform domain and your custom domains with status (PENDING, VERIFIED, FAILED), isDefault, linkCount and the DNS records to configure. Use a domain’s id as domainId when creating links.

Errors

Errors use conventional HTTP status codes and a consistent body. fieldErrors maps field paths to messages for validation failures. Include the requestId when contacting support.

httpError response
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "statusCode": 400,
  "code": "VALIDATION_FAILED",
  "message": "Some fields are invalid",
  "fieldErrors": {
    "destinations.0.url": ["Enter a valid https:// URL"]
  },
  "requestId": "req_7f3c2a9e41b0"
}
HTTP status codes
400The request is malformed or failed validation (code VALIDATION_FAILED).
401Missing, invalid, expired or revoked API key.
402The feature or limit requires a higher plan (FEATURE_NOT_IN_PLAN or PLAN_LIMIT_REACHED).
403The key lacks the required scope.
404The resource does not exist in this workspace.
409Conflict, such as a slug that is already taken.
429Rate limit exceeded (RATE_LIMITED) — wait Retry-After seconds.
5xxSomething went wrong on our side. Retry with backoff.

Rate limits

Each API key can make 600 requests per minute. Every response includes RateLimit-Limit and RateLimit-Remaining headers. When you exceed the limit you receive 429 with a Retry-After header in seconds.

httpRate limit headers
HTTP/1.1 200 OK
RateLimit-Limit: 600
RateLimit-Remaining: 587

HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 600
RateLimit-Remaining: 0
Retry-After: 23

Examples

Create a link that sends iPhone and Android users to their stores, routes visitors in Germany to a localised page, and tags every visit with UTM parameters.

bashcURL
curl -X POST https://1link.1workspace.in/api/v1/links \
  -H "Authorization: Bearer fk_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Summer launch",
  "defaultUrl": "https://example.com/app",
  "destinations": [
    {
      "platform": "IOS",
      "url": "https://apps.apple.com/app/id1234567890"
    },
    {
      "platform": "ANDROID",
      "url": "https://play.google.com/store/apps/details?id=com.example.app"
    }
  ],
  "rules": [
    {
      "name": "Germany",
      "countries": [
        "DE"
      ],
      "destinationUrl": "https://example.com/de/app"
    }
  ],
  "utm": {
    "source": "newsletter",
    "medium": "email",
    "campaign": "summer-launch"
  }
}'
jsJavaScript (Node 18+ / fetch)
const res = await fetch('https://1link.1workspace.in/api/v1/links', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.FORKLY_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "Summer launch",
    "defaultUrl": "https://example.com/app",
    "destinations": [
      {
        "platform": "IOS",
        "url": "https://apps.apple.com/app/id1234567890"
      },
      {
        "platform": "ANDROID",
        "url": "https://play.google.com/store/apps/details?id=com.example.app"
      }
    ],
    "rules": [
      {
        "name": "Germany",
        "countries": [
          "DE"
        ],
        "destinationUrl": "https://example.com/de/app"
      }
    ],
    "utm": {
      "source": "newsletter",
      "medium": "email",
      "campaign": "summer-launch"
    }
  }),
});

if (!res.ok) {
  const error = await res.json(); // { statusCode, code, message, fieldErrors, requestId }
  throw new Error(`${error.code}: ${error.message}`);
}

const link = await res.json();
console.log(link.shortUrl);

Questions or feedback about the API? Contact our team.