Zulk / zulk-api.yaml
Milindu Kumarage
Initial
393823d
openapi: 3.0.0
info:
version: 1.0.0
title: Zulk API
description: URL shortener API for Zulk platform
servers:
- url: https://api.zu.lk
description: Production server
- url: http://localhost:8787
description: Development server
tags:
- name: Authentication
description: πŸ” OAuth endpoints for web app authentication (no API key required)
- name: API v1 - Users
description: πŸ‘₯ User management endpoints (requires API key)
- name: API v1 - Organizations
description: 🏒 Organization management endpoints (requires API key)
- name: API v1 - Links
description: πŸ”— URL shortening endpoints (requires API key)
- name: API v1 - Analytics
description: πŸ“Š Analytics and reporting endpoints (requires API key)
components:
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-KEY
description: API Key for authentication
ApiSecretAuth:
type: apiKey
in: header
name: X-API-Secret
description: API Secret for authentication
schemas:
Link:
type: object
properties:
id:
type: string
example: "1"
description: Link ID
key:
type: string
example: abc123
description: Short link key
url:
type: string
format: uri
example: https://example.com
description: Original URL
created_at:
type: string
format: date-time
example: 2025-01-01T00:00:00Z
description: Creation timestamp
owner:
type: string
example: org123
description: Organization ID that owns this link
CreateLink:
type: object
required:
- url
properties:
key:
type: string
example: custom-key
description: Custom short key (optional)
url:
type: string
format: uri
example: https://example.com
description: URL to shorten
length:
type: integer
minimum: 3
maximum: 10
example: 6
description: "Length of generated key (3-10, default: 6)"
CreateLinkResponse:
type: object
properties:
message:
type: string
example: Record added successfully
created_at:
type: string
format: date-time
example: 2025-01-01T00:00:00Z
shortLink:
type: string
example: https://zu.lk/abc123
key:
type: string
example: abc123
url:
type: string
format: uri
example: https://example.com
length:
type: integer
example: 6
Error:
type: object
properties:
error:
type: string
example: Error message
description: Error message
User:
type: object
properties:
id:
type: string
example: "123"
description: User ID
name:
type: string
example: John Doe
description: User name
age:
type: integer
example: 42
description: User age
AuthUser:
type: object
properties:
email:
type: string
format: email
example: user@example.com
description: User email (lowercased)
user:
type: object
properties:
id:
type: string
example: "123456789"
description: Google user ID
name:
type: string
example: John Doe
description: User display name
email:
type: string
format: email
example: user@example.com
description: User email from Google
picture:
type: string
format: uri
example: https://lh3.googleusercontent.com/...
description: User profile picture URL
description: Google OAuth user data
token:
type: string
example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
description: JWT token
apiKey:
type: string
example: ak_1234567890abcdef
description: User API key
apiToken:
type: string
example: at_1234567890abcdef
description: User API token
Organization:
type: object
properties:
id:
type: number
example: 1
description: Organization ID
name:
type: string
example: Acme Corp
description: Organization name
role:
type: string
example: OWNER
description: User role in the organization
OrganizationMember:
type: object
properties:
id:
type: number
example: 123
description: User ID
name:
type: string
example: John Doe
description: User name
email:
type: string
format: email
example: john@example.com
description: User email
role:
type: string
enum:
- MANAGER
- ADMIN
- OWNER
example: MANAGER
description: User role in the organization
AddMemberRequest:
type: object
required:
- email
properties:
email:
type: string
format: email
example: newmember@example.com
description: Email of the user to add
role:
type: string
enum:
- MANAGER
- ADMIN
- OWNER
example: MANAGER
default: MANAGER
description: Role to assign to the new member
UpdateRoleRequest:
type: object
required:
- role
properties:
role:
type: string
enum:
- MANAGER
- ADMIN
- OWNER
example: ADMIN
description: New role for the member
AnalyticsResponse:
type: object
properties:
orgId:
type: string
example: "1"
description: Organization ID
analytics:
type: object
properties:
totalClicks:
type: number
example: 1250
description: Total click count
dateRange:
type: object
properties:
from:
type: string
example: -7d
description: Start date of the range
to:
type: string
example: today
description: End date of the range
interval:
type: string
example: day
description: Data interval
data:
type: array
items:
type: number
example:
- 120
- 150
- 89
- 200
- 180
- 145
- 220
description: Click data points
labels:
type: array
items:
type: string
example:
- 2025-08-09
- 2025-08-10
- 2025-08-11
- 2025-08-12
- 2025-08-13
- 2025-08-14
- 2025-08-15
description: Date labels for data points
generatedAt:
type: string
format: date-time
example: 2025-08-15T12:00:00Z
description: Timestamp when analytics were generated
UpdateLinkRequest:
type: object
required:
- url
- key
properties:
url:
type: string
format: uri
example: https://updated-example.com
description: Updated URL
key:
type: string
example: updated-key
description: Updated short key
CreateOrganization:
type: object
required:
- name
properties:
name:
type: string
example: Acme Corp
description: Organization name
security:
- ApiKeyAuth: []
ApiSecretAuth: []
paths:
/v1/organizations:
get:
summary: Get user organizations
description: Retrieve organizations that the authenticated user has access to
tags:
- API v1 - Organizations
security:
- ApiKeyAuth: []
ApiSecretAuth: []
responses:
"200":
description: Successfully retrieved user organizations
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Organization"
"401":
description: Unauthorized - Invalid API credentials
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a new organization
description: Create a new organization and assign the authenticated user as owner
tags:
- API v1 - Organizations
security:
- ApiKeyAuth: []
ApiSecretAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateOrganization"
responses:
"200":
description: Successfully created organization
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Organization created successfully
id:
type: number
example: 1
description: Organization ID
name:
type: string
example: Acme Corp
description: Organization name
role:
type: string
example: OWNER
description: User role in the organization
"400":
description: Bad request - Name is required
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"401":
description: Unauthorized - Invalid API credentials
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"500":
description: Internal server error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/v1/organizations/{orgId}/members:
get:
summary: Get organization members
description: Retrieve all members of a specific organization
tags:
- API v1 - Organizations
security:
- ApiKeyAuth: []
ApiSecretAuth: []
parameters:
- name: orgId
in: path
required: true
schema:
type: string
description: Organization ID
example: "1"
responses:
"200":
description: Successfully retrieved organization members
content:
application/json:
schema:
type: object
properties:
members:
type: array
items:
$ref: "#/components/schemas/OrganizationMember"
total:
type: number
example: 5
description: Total number of members
"401":
description: Unauthorized - Invalid API credentials
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"403":
description: Access denied to this organization
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Add member to organization
description: Add a new member to an organization (requires ADMIN or OWNER role)
tags:
- API v1 - Organizations
security:
- ApiKeyAuth: []
ApiSecretAuth: []
parameters:
- name: orgId
in: path
required: true
schema:
type: string
description: Organization ID
example: "1"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/AddMemberRequest"
responses:
"200":
description: Successfully added member to organization
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Member added successfully
member:
$ref: "#/components/schemas/OrganizationMember"
"400":
description: Bad request - Email is required or invalid role
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"401":
description: Unauthorized - Invalid API credentials
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"403":
description: Access denied - Requires ADMIN or OWNER role
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"404":
description: User not found - User must register first
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
error: User not found. User must register first.
"409":
description: Conflict - User is already a member
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"500":
description: Internal server error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/v1/organizations/{orgId}/members/{memberId}/role:
patch:
summary: Update member role
description: Update the role of a specific member in an organization (requires
ADMIN or OWNER role)
tags:
- API v1 - Organizations
security:
- ApiKeyAuth: []
ApiSecretAuth: []
parameters:
- name: orgId
in: path
required: true
schema:
type: string
description: Organization ID
example: "1"
- name: memberId
in: path
required: true
schema:
type: string
description: Member ID
example: "123"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateRoleRequest"
responses:
"200":
description: Successfully updated member role
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Member role updated successfully
member:
type: object
properties:
id:
type: number
example: 123
name:
type: string
example: John Doe
email:
type: string
example: john@example.com
role:
type: string
example: ADMIN
previousRole:
type: string
example: MANAGER
"400":
description: Bad request - Role is required or invalid
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"401":
description: Unauthorized - Invalid API credentials
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"403":
description: Access denied - Requires ADMIN or OWNER role, or cannot change own
role
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"404":
description: Member not found in this organization
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
error: Member not found in this organization
"500":
description: Internal server error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/v1/organizations/{orgId}/members/{memberId}:
delete:
summary: Remove member from organization
description: Remove a member from an organization (requires ADMIN or OWNER
role). Cannot remove yourself or the current OWNER.
tags:
- API v1 - Organizations
security:
- ApiKeyAuth: []
ApiSecretAuth: []
parameters:
- name: orgId
in: path
required: true
schema:
type: string
description: Organization ID
example: "1"
- name: memberId
in: path
required: true
schema:
type: string
description: Member ID
example: "123"
responses:
"200":
description: Successfully removed member from organization
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Member removed successfully
removedMember:
$ref: "#/components/schemas/OrganizationMember"
"401":
description: Unauthorized - Invalid API credentials
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"403":
description: Access denied - Requires ADMIN or OWNER role, cannot remove
yourself, or cannot remove last OWNER
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"404":
description: Member not found in this organization
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
error: Member not found in this organization
"500":
description: Internal server error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/v1/organizations/{orgId}/analytics/clicks:
get:
summary: Get organization click analytics
description: Retrieve click analytics data for an organization's links from PostHog
tags:
- API v1 - Analytics
security:
- ApiKeyAuth: []
ApiSecretAuth: []
parameters:
- name: orgId
in: path
required: true
schema:
type: string
description: Organization ID
example: "1"
- name: date_from
in: query
required: false
schema:
type: string
description: "Start date for analytics (default: -7d)"
example: -7d
- name: date_to
in: query
required: false
schema:
type: string
description: "End date for analytics (default: today)"
example: today
- name: interval
in: query
required: false
schema:
type: string
description: "Data interval (default: day)"
example: day
responses:
"200":
description: Successfully retrieved analytics data
content:
application/json:
schema:
$ref: "#/components/schemas/AnalyticsResponse"
"401":
description: Unauthorized - Invalid API credentials
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"403":
description: Access denied to this organization
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"500":
description: Internal server error - Failed to fetch analytics data
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/v1/organizations/{orgId}/links:
get:
summary: Get organization links
description: Retrieve all links for a specific organization
tags:
- API v1 - Links
security:
- ApiKeyAuth: []
ApiSecretAuth: []
parameters:
- name: orgId
in: path
required: true
schema:
type: string
description: Organization ID
example: "1"
responses:
"200":
description: Successfully retrieved organization links
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/Link"
"401":
description: Unauthorized - Invalid API credentials
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"403":
description: Access denied to this organization
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a new link in organization
description: Create a new short link for the given URL in the specified organization
tags:
- API v1 - Links
security:
- ApiKeyAuth: []
ApiSecretAuth: []
parameters:
- name: orgId
in: path
required: true
schema:
type: string
description: Organization ID
example: "1"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateLink"
responses:
"200":
description: Successfully created short link
content:
application/json:
schema:
$ref: "#/components/schemas/CreateLinkResponse"
"401":
description: Unauthorized - Invalid API credentials
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"403":
description: Access denied to this organization
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"500":
description: Internal server error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/v1/organizations/{orgId}/links/{id}:
get:
summary: Get a specific link from organization
description: Retrieve a specific link by ID from the specified organization
tags:
- API v1 - Links
security:
- ApiKeyAuth: []
ApiSecretAuth: []
parameters:
- name: orgId
in: path
required: true
schema:
type: string
description: Organization ID
example: "1"
- name: id
in: path
required: true
schema:
type: string
description: Link ID
example: "1"
responses:
"200":
description: Successfully retrieved link
content:
application/json:
schema:
$ref: "#/components/schemas/Link"
"401":
description: Unauthorized - Invalid API credentials
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"403":
description: Access denied to this organization
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"404":
description: Link not found
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
error: Link not found
/v1/organizations/{orgId}/links/{linkId}:
put:
summary: Update a link in organization
description: Update an existing short link for the specified organization
tags:
- API v1 - Links
security:
- ApiKeyAuth: []
ApiSecretAuth: []
parameters:
- name: orgId
in: path
required: true
schema:
type: string
description: Organization ID
example: "1"
- name: linkId
in: path
required: true
schema:
type: string
description: Link ID
example: "1"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/UpdateLinkRequest"
responses:
"200":
description: Successfully updated link
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Link updated successfully
id:
type: string
example: "1"
description: Link ID
key:
type: string
example: updated-key
description: Updated short key
url:
type: string
format: uri
example: https://updated-example.com
description: Updated URL
shortLink:
type: string
example: https://zu.lk/updated-key
description: Full short link URL
updated_at:
type: string
format: date-time
example: 2025-08-15T12:00:00Z
description: Update timestamp
previousKey:
type: string
example: old-key
description: Previous short key
"400":
description: Bad request - URL and key are required
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"401":
description: Unauthorized - Invalid API credentials
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"403":
description: Access denied to this organization
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"404":
description: Link not found in this organization
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
error: Link not found in this organization
"409":
description: Conflict - Key is already taken
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"500":
description: Internal server error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"