504 lines
13 KiB
YAML
504 lines
13 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: Bit - URL Shortener API
|
|
description: |
|
|
Fast, lightweight, self-hosted URL shortener service with minimal click tracking.
|
|
|
|
## Getting Started
|
|
|
|
For setup instructions, please check the [README](https://github.com/sjdonado/bit/blob/master/README.md).
|
|
|
|
## Authentication
|
|
|
|
Multiple users are supported via `X-Api-Key` headers. Create, list and delete keys via the [CLI](https://github.com/sjdonado/bit/blob/master/SETUP.md#cli).
|
|
version: 1.6.0
|
|
contact:
|
|
name: Juan Rodriguez Donado
|
|
url: https://sjdonado.com
|
|
|
|
servers:
|
|
- url: http://localhost:4000
|
|
description: Development server
|
|
|
|
security:
|
|
- ApiKeyAuth: []
|
|
|
|
paths:
|
|
/api/ping:
|
|
get:
|
|
summary: Ping the API
|
|
description: Health check endpoint to verify the API is running
|
|
operationId: ping
|
|
tags:
|
|
- Health
|
|
security: []
|
|
responses:
|
|
'200':
|
|
description: API is healthy
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
data:
|
|
type: string
|
|
example: pong
|
|
|
|
/{slug}:
|
|
get:
|
|
summary: Redirect by slug
|
|
description: Redirects to the original URL and tracks the click asynchronously
|
|
operationId: redirectBySlug
|
|
tags:
|
|
- Redirects
|
|
security: []
|
|
parameters:
|
|
- name: slug
|
|
in: path
|
|
required: true
|
|
description: The short URL slug
|
|
schema:
|
|
type: string
|
|
example: 3wP4BQ
|
|
- name: utm_source
|
|
in: query
|
|
required: false
|
|
description: UTM source parameter for tracking
|
|
schema:
|
|
type: string
|
|
example: email_campaign
|
|
responses:
|
|
'301':
|
|
description: Redirect to original URL
|
|
headers:
|
|
Location:
|
|
description: The original URL
|
|
schema:
|
|
type: string
|
|
example: https://example.com
|
|
X-Forwarded-For:
|
|
description: Client IP address
|
|
schema:
|
|
type: string
|
|
User-Agent:
|
|
description: User agent string
|
|
schema:
|
|
type: string
|
|
'404':
|
|
description: Link not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
/api/links:
|
|
get:
|
|
summary: List all links
|
|
description: Retrieve all links for the authenticated user with pagination support
|
|
operationId: listLinks
|
|
tags:
|
|
- Links
|
|
parameters:
|
|
- name: limit
|
|
in: query
|
|
description: Number of results per page
|
|
schema:
|
|
type: integer
|
|
default: 100
|
|
minimum: 1
|
|
maximum: 1000
|
|
- name: cursor
|
|
in: query
|
|
description: Pagination cursor from previous response
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: List of links
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
data:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/LinkSummary'
|
|
pagination:
|
|
$ref: '#/components/schemas/Pagination'
|
|
'401':
|
|
description: Unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
post:
|
|
summary: Create new link
|
|
description: Create a new shortened link
|
|
operationId: createLink
|
|
tags:
|
|
- Links
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required:
|
|
- url
|
|
properties:
|
|
url:
|
|
type: string
|
|
format: uri
|
|
description: The URL to shorten
|
|
example: https://example.com
|
|
responses:
|
|
'201':
|
|
description: Link created successfully
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
data:
|
|
$ref: '#/components/schemas/Link'
|
|
'400':
|
|
description: Bad request - invalid URL or missing field
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
examples:
|
|
missingField:
|
|
value:
|
|
error: "url: Required field"
|
|
invalidUrl:
|
|
value:
|
|
errors:
|
|
url:
|
|
- is invalid
|
|
'401':
|
|
description: Unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
/api/links/{id}:
|
|
get:
|
|
summary: Get link by ID
|
|
description: Retrieve a specific link with up to 100 most recent clicks. For complete click history, use /api/links/{id}/clicks
|
|
operationId: getLink
|
|
tags:
|
|
- Links
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Link ID
|
|
schema:
|
|
type: integer
|
|
format: int64
|
|
responses:
|
|
'200':
|
|
description: Link details
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
data:
|
|
$ref: '#/components/schemas/Link'
|
|
'404':
|
|
description: Link not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
'401':
|
|
description: Unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
put:
|
|
summary: Update link
|
|
description: Update the URL of an existing link
|
|
operationId: updateLink
|
|
tags:
|
|
- Links
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Link ID
|
|
schema:
|
|
type: integer
|
|
format: int64
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required:
|
|
- url
|
|
properties:
|
|
url:
|
|
type: string
|
|
format: uri
|
|
description: The new URL
|
|
example: https://newexample.com
|
|
responses:
|
|
'200':
|
|
description: Link updated successfully
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
data:
|
|
$ref: '#/components/schemas/Link'
|
|
'400':
|
|
description: Bad request
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
'401':
|
|
description: Unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
'403':
|
|
description: Forbidden - link belongs to another user
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
'404':
|
|
description: Link not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
delete:
|
|
summary: Delete link
|
|
description: Delete a link and all its associated clicks
|
|
operationId: deleteLink
|
|
tags:
|
|
- Links
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Link ID
|
|
schema:
|
|
type: integer
|
|
format: int64
|
|
responses:
|
|
'204':
|
|
description: Link deleted successfully
|
|
'401':
|
|
description: Unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
'403':
|
|
description: Forbidden - link belongs to another user
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
'404':
|
|
description: Link not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
/api/links/{id}/clicks:
|
|
get:
|
|
summary: List clicks for a link
|
|
description: Retrieve all clicks for a specific link with pagination support
|
|
operationId: listClicks
|
|
tags:
|
|
- Clicks
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
description: Link ID
|
|
schema:
|
|
type: integer
|
|
format: int64
|
|
- name: limit
|
|
in: query
|
|
description: Number of results per page
|
|
schema:
|
|
type: integer
|
|
default: 100
|
|
minimum: 1
|
|
maximum: 1000
|
|
- name: cursor
|
|
in: query
|
|
description: Pagination cursor from previous response
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: List of clicks
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
data:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Click'
|
|
pagination:
|
|
$ref: '#/components/schemas/Pagination'
|
|
'401':
|
|
description: Unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
'404':
|
|
description: Link not found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Error'
|
|
|
|
components:
|
|
securitySchemes:
|
|
ApiKeyAuth:
|
|
type: apiKey
|
|
in: header
|
|
name: X-Api-Key
|
|
description: API key for authentication
|
|
|
|
schemas:
|
|
LinkSummary:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: integer
|
|
format: int64
|
|
description: Unique link identifier
|
|
example: 1
|
|
refer:
|
|
type: string
|
|
format: uri
|
|
description: The shortened URL
|
|
example: http://localhost:4000/3wP4BQ
|
|
origin:
|
|
type: string
|
|
format: uri
|
|
description: The original URL
|
|
example: https://monocuco.donado.co
|
|
|
|
Link:
|
|
allOf:
|
|
- $ref: '#/components/schemas/LinkSummary'
|
|
- type: object
|
|
properties:
|
|
clicks:
|
|
type: array
|
|
description: Array of click records (up to 100 most recent)
|
|
items:
|
|
$ref: '#/components/schemas/Click'
|
|
|
|
Click:
|
|
type: object
|
|
properties:
|
|
id:
|
|
type: integer
|
|
format: int64
|
|
description: Unique click identifier
|
|
example: 1
|
|
user_agent:
|
|
type: string
|
|
description: User agent string
|
|
example: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:127.0) Gecko/20100101 Firefox/127.0
|
|
country:
|
|
type: string
|
|
description: Country code (ISO 3166-1 alpha-2)
|
|
example: US
|
|
nullable: true
|
|
browser:
|
|
type: string
|
|
description: Browser name
|
|
example: Firefox
|
|
nullable: true
|
|
os:
|
|
type: string
|
|
description: Operating system
|
|
example: Mac OS X
|
|
nullable: true
|
|
referer:
|
|
type: string
|
|
description: Referer domain or utm_source
|
|
example: Direct
|
|
nullable: true
|
|
created_at:
|
|
type: string
|
|
format: date-time
|
|
description: Click timestamp
|
|
example: 2024-07-12T19:25:22Z
|
|
|
|
Pagination:
|
|
type: object
|
|
properties:
|
|
has_more:
|
|
type: boolean
|
|
description: Whether there are more results
|
|
example: true
|
|
next:
|
|
type: integer
|
|
format: int64
|
|
description: Cursor for next page (link/click ID)
|
|
example: 12
|
|
nullable: true
|
|
|
|
Error:
|
|
type: object
|
|
properties:
|
|
error:
|
|
type: string
|
|
description: Error message
|
|
example: Resource not found
|
|
required:
|
|
- error
|
|
|
|
ValidationErrors:
|
|
type: object
|
|
properties:
|
|
errors:
|
|
type: object
|
|
additionalProperties:
|
|
type: array
|
|
items:
|
|
type: string
|
|
description: Field-level validation errors
|
|
example:
|
|
url:
|
|
- is invalid
|
|
|
|
tags:
|
|
- name: Health
|
|
description: Health check endpoints
|
|
- name: Redirects
|
|
description: URL redirection and click tracking
|
|
- name: Links
|
|
description: Link management operations
|
|
- name: Clicks
|
|
description: Click analytics and tracking
|