Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 175 additions & 0 deletions apps/docs/content/docs/en/tools/short_io.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
---
title: Short.io
description: Create and manage short links, domains, QR codes, and analytics
---

import { BlockInfoCard } from "@/components/ui/block-info-card"

<BlockInfoCard
type="short_io"
color="#0BB07D"
/>

{/* MANUAL-CONTENT-START:intro */}
[Short.io](https://short.io/) is a white-label URL shortener that lets you create branded short links on your own domain, track clicks, and manage links at scale. Short.io is designed for businesses that want professional short URLs, QR codes, and link analytics without relying on generic shorteners.

With Short.io in Sim, you can:

- **Create short links**: Generate branded short URLs from long URLs using your custom domain, with optional custom paths
- **List domains**: Retrieve all Short.io domains on your account to get domain IDs for listing links
- **List links**: List short links for a domain with pagination and optional date sort order
- **Delete links**: Remove a short link by its ID (e.g. lnk_abc123_abcdef)
- **Generate QR codes**: Create QR codes for any Short.io link with optional size, color, background color, and format (PNG or SVG); returns a base64 data URL
- **Get link statistics**: Fetch click analytics for a link including total clicks, human clicks, referrer/country/browser/OS/city breakdowns, UTM dimensions, time-series data, and date interval

These capabilities allow your Sim agents to automate link shortening, QR code generation, and analytics reporting directly in your workflows — from campaign tracking to link management and performance dashboards.
{/* MANUAL-CONTENT-END */}

## Usage Instructions

Integrate Short.io into your workflow for link shortening, QR codes, and analytics. Authenticate with your Short.io Secret API Key. Create links, list domains and links, delete links, generate QR codes, and fetch per-link statistics.

## Tools

### `short_io_create_link`

Create a short link using your Short.io custom domain.

#### Input

| Parameter | Type | Required | Description |
| ------------- | ------ | -------- | ---------------------------------------------------------- |
| `domain` | string | Yes | Your registered Short.io custom domain |
| `originalURL` | string | Yes | The long URL to shorten |
| `path` | string | No | Optional custom path for the short link |
| `apiKey` | string | Yes | Short.io Secret API Key |

#### Output

| Parameter | Type | Description |
| ---------- | ------- | ---------------------------------- |
| `success` | boolean | Whether the link was created |
| `shortURL` | string | The generated short link URL |
| `idString` | string | The unique Short.io link ID |
| `error` | string | Error message if failed |

### `short_io_list_domains`

List Short.io domains. Returns domain IDs and details for use in List Links.

#### Input

| Parameter | Type | Required | Description |
| ------------- | ------ | -------- | ------------------------------- |
| `apiKey` | string | Yes | Short.io Secret API Key |

#### Output

| Parameter | Type | Description |
| --------- | ------- | ------------------------------------ |
| `success` | boolean | Success status |
| `domains` | array | List of domain objects (id, hostname) |
| `count` | number | Number of domains |
| `error` | string | Error message |

### `short_io_list_links`

List short links for a domain. Requires domain_id from List Domains. Max 150 per request.

#### Input

| Parameter | Type | Required | Description |
| --------------- | ------ | -------- | ------------------------------------ |
| `domainId` | number | Yes | Domain ID (from List Domains) |
| `limit` | number | No | Max links to return (1–150) |
| `pageToken` | string | No | Pagination token from previous call |
| `dateSortOrder`| string | No | Sort by date: asc or desc |
| `apiKey` | string | Yes | Short.io Secret API Key |

#### Output

| Parameter | Type | Description |
| --------------- | ------- | ---------------------------------------- |
| `success` | boolean | Success status |
| `links` | array | List of link objects (idString, shortURL, originalURL, path, etc.) |
| `count` | number | Number of links returned |
| `nextPageToken` | string | Token for next page |
| `error` | string | Error message |

### `short_io_delete_link`

Delete a short link by ID. Rate limit 20/s.

#### Input

| Parameter | Type | Required | Description |
| ------------- | ------ | -------- | ------------------------------- |
| `linkId` | string | Yes | Link ID to delete (e.g. lnk_abc123_abcdef) |
| `apiKey` | string | Yes | Short.io Secret API Key |

#### Output

| Parameter | Type | Description |
| --------- | ------- | -------------------------- |
| `success` | boolean | Success status |
| `deleted` | boolean | Whether the link was deleted |
| `idString`| string | Deleted link ID |
| `error` | string | Error message |

### `short_io_get_qr_code`

Generate a QR code for a Short.io link. Returns a base64 data URL (e.g. data:image/png;base64,...).

#### Input

| Parameter | Type | Required | Description |
| --------------- | ------ | -------- | ------------------------------------ |
| `linkId` | string | Yes | Link ID (e.g. lnk_abc123_abcdef) |
| `color` | string | No | QR color hex (e.g. 000000) |
| `backgroundColor` | string | No | Background color hex (e.g. FFFFFF) |
| `size` | number | No | QR size 1–99 |
| `type` | string | No | Output format: png or svg |
| `apiKey` | string | Yes | Short.io Secret API Key |

#### Output

| Parameter | Type | Description |
| ---------- | ------- | ------------------------------------------------ |
| `file` | file | Generated QR code image file |

### `short_io_get_analytics`

Fetch click statistics for a Short.io link.

#### Input

| Parameter | Type | Required | Description |
| ------------- | ------ | -------- | -------------------------------------------------------- |
| `linkId` | string | Yes | Link ID |
| `period` | string | Yes | Period: today, yesterday, last7, last30, total, week, month, lastmonth |
| `tz` | string | No | Timezone (default UTC) |
| `apiKey` | string | Yes | Short.io Secret API Key |

#### Output

| Parameter | Type | Description |
| ------------------- | ------- | ------------------------------------------------------ |
| `success` | boolean | Success status |
| `clicks` | number | Total clicks in period |
| `totalClicks` | number | Total clicks |
| `humanClicks` | number | Human clicks |
| `totalClicksChange` | string | Change vs previous period |
| `humanClicksChange` | string | Human clicks change |
| `referer` | array | Referrer breakdown (referer, score) |
| `country` | array | Country breakdown (countryName, country, score) |
| `browser` | array | Browser breakdown (browser, score) |
| `os` | array | OS breakdown (os, score) |
| `city` | array | City breakdown (city, name, countryCode, score) |
| `device` | array | Device breakdown |
| `social` | array | Social source breakdown (social, score) |
| `utmMedium` | array | UTM medium breakdown |
| `utmSource` | array | UTM source breakdown |
| `utmCampaign` | array | UTM campaign breakdown |
| `clickStatistics` | object | Time-series click data (datasets with x/y per interval) |
| `interval` | object | Date range (startDate, endDate, prevStartDate, prevEndDate, tz) |
| `error` | string | Error message |
101 changes: 101 additions & 0 deletions apps/sim/app/api/tools/short_io/qr/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { createLogger } from '@sim/logger'
import { type NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateRequestId } from '@/lib/core/utils/request'

export const dynamic = 'force-dynamic'

const logger = createLogger('ShortIoQrAPI')

const ShortIoQrSchema = z.object({
apiKey: z.string().min(1, 'API key is required'),
linkId: z.string().min(1, 'Link ID is required'),
color: z.string().optional(),
backgroundColor: z.string().optional(),
size: z.number().min(1).max(99).optional(),
type: z.enum(['png', 'svg']).optional(),
useDomainSettings: z.boolean().optional(),
})

export async function POST(request: NextRequest) {
const requestId = generateRequestId()

try {
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })

if (!authResult.success) {
logger.warn(`[${requestId}] Unauthorized Short.io QR request: ${authResult.error}`)
return NextResponse.json(
{ success: false, error: authResult.error || 'Authentication required' },
{ status: 401 }
)
}

const body = await request.json()
const validated = ShortIoQrSchema.parse(body)

const qrBody: Record<string, unknown> = {
useDomainSettings: validated.useDomainSettings ?? true,
}
if (validated.color) qrBody.color = validated.color
if (validated.backgroundColor) qrBody.backgroundColor = validated.backgroundColor
if (validated.size) qrBody.size = validated.size
if (validated.type) qrBody.type = validated.type

const response = await fetch(`https://api.short.io/links/qr/${validated.linkId}`, {
method: 'POST',
headers: {
Authorization: validated.apiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify(qrBody),
})

if (!response.ok) {
const errorText = await response.text().catch(() => response.statusText)
logger.error(`[${requestId}] Short.io QR API error: ${errorText}`)
return NextResponse.json(
{ success: false, error: `Short.io API error: ${errorText}` },
{ status: response.status }
)
}

const contentType = response.headers.get('Content-Type') ?? 'image/png'
const fileBuffer = Buffer.from(await response.arrayBuffer())
const mimeType = contentType.split(';')[0]?.trim() || 'image/png'
const ext = validated.type === 'svg' ? 'svg' : 'png'
const fileName = `qr-${validated.linkId}.${ext}`

logger.info(`[${requestId}] QR code generated`, {
linkId: validated.linkId,
size: fileBuffer.length,
mimeType,
})

return NextResponse.json({
success: true,
output: {
file: {
name: fileName,
mimeType,
data: fileBuffer.toString('base64'),
size: fileBuffer.length,
},
},
})
} catch (error: unknown) {
if (error instanceof z.ZodError) {
return NextResponse.json(
{
success: false,
error: `Validation error: ${error.errors.map((e) => e.message).join(', ')}`,
},
{ status: 400 }
)
}
const message = error instanceof Error ? error.message : 'Unknown error'
logger.error(`[${requestId}] Short.io QR error: ${message}`)
return NextResponse.json({ success: false, error: message }, { status: 500 })
}
}
Loading
Loading