Public API Integration
Use LimeLink's public server-to-server API to automate Projects, Applications, Custom Domains, and Links with an Organization API credential.
- Swagger UI — browse the public API reference
- OpenAPI JSON — machine-readable contract for tooling and code generation
Swagger/OpenAPI is the authority for exact parameters, request and response schemas, examples, and operation-specific errors. This guide explains authentication, scopes, ownership, and safe integration workflows.
Authentication
Create a credential from the Organization workspace under API credentials, select only the scopes your integration needs, and copy the secret from its one-time reveal. Store it in a server-side secret manager or environment variable.
export LIMELINK_API_KEY='replace-with-the-one-time-secret'
Send it with every request:
X-API-KEY: your_api_key
Never put the credential in browser JavaScript, a mobile app, source control, logs, screenshots, or public AI prompts. Native and cross-platform SDKs use a public Project UUID instead; they do not use this Organization secret.
Scopes
Read and Write scopes are independent. Write access does not imply Read access.
| Resource | Read | Write |
|---|---|---|
| Projects | projects:read | projects:write |
| Applications | applications:read | applications:write |
| Custom Domains | domains:read | domains:write |
| Links | links:read | links:write |
A missing required scope returns 403. Credentials are Organization-scoped: referenced Projects and their Applications, Custom Domains, and Links must belong to that Organization. Missing and outside-Organization resources return a non-disclosing 404.
Public operation inventory
The filtered public OpenAPI contract contains these 30 API-key operations. Bearer-only console, authentication, billing, attachment, webhook, Stats, Deferred, Device, and Universal Link routes are not part of this contract.
API Credentials
| Method | Path | Scope |
|---|---|---|
GET | /api/v2/api-credentials/current | None; valid API key required |
Core Link
| Method | Path | Scope |
|---|---|---|
POST | /api/v2/core/link | links:write |
GET | /api/v2/core/link/availability | links:read |
Projects
| Method | Path | Scope |
|---|---|---|
POST | /api/v2/organizations/{organization_id}/projects | projects:write |
GET | /api/v2/organizations/{organization_id}/projects | projects:read |
GET | /api/v2/organizations/{organization_id}/projects/name-availability | projects:read |
GET | /api/v2/organizations/{organization_id}/projects/{project_id} | projects:read |
PATCH | /api/v2/organizations/{organization_id}/projects/{project_id} | projects:write |
DELETE | /api/v2/organizations/{organization_id}/projects/{project_id} | projects:write |
Project Skin
| Method | Path | Scope |
|---|---|---|
GET | /api/v2/organizations/{organization_id}/projects/{project_id}/skin | projects:read |
PUT | /api/v2/organizations/{organization_id}/projects/{project_id}/skin | projects:write |
Project Skin stores theme configuration for landing pages. The PUT body is theme-only: theme_type is basic or custom, and theme_choice_type is one of the documented theme choices. Landing copy such as default_hookup_msg is Project-owned and changed through Project create/update, not through Skin PUT. GET may return 404 Skin not found. when the Project exists but no Skin row exists.
Applications
| Method | Path | Scope |
|---|---|---|
POST | /api/v2/organizations/{organization_id}/projects/{project_id}/applications | applications:write |
GET | /api/v2/organizations/{organization_id}/projects/{project_id}/applications | applications:read |
GET | /api/v2/applications/{application_id} | applications:read |
PATCH | /api/v2/applications/{application_id} | applications:write |
DELETE | /api/v2/applications/{application_id} | applications:write |
Custom Domains
| Method | Path | Scope |
|---|---|---|
POST | /api/v2/organizations/{organization_id}/projects/{project_id}/custom-domains | domains:write |
GET | /api/v2/organizations/{organization_id}/projects/{project_id}/custom-domains | domains:read |
GET | /api/v2/custom-domains/{custom_domain_id} | domains:read |
POST | /api/v2/custom-domains/{custom_domain_id}/retry | domains:write |
DELETE | /api/v2/custom-domains/{custom_domain_id} | domains:write |
Links
| Method | Path | Scope |
|---|---|---|
POST | /api/v2/organizations/{organization_id}/projects/{project_id}/links | links:write |
GET | /api/v2/organizations/{organization_id}/projects/{project_id}/links | links:read |
POST | /api/v2/custom-domains/{custom_domain_id}/links | links:write |
GET | /api/v2/custom-domains/{custom_domain_id}/links | links:read |
POST | /api/v2/links | links:write |
GET | /api/v2/links/resolve?url={canonical_url} | links:read |
GET | /api/v2/links/{link_id} | links:read |
PATCH | /api/v2/links/{link_id} | links:write |
DELETE | /api/v2/links/{link_id} | links:write |
First request
Use an Organization ID available to the credential and consult Swagger for the current parameters and response:
curl 'https://api.limelink.org/api/v2/organizations/22222222-2222-4222-8222-222222222222/projects' \
--header "X-API-KEY: $LIMELINK_API_KEY"
Create a Project with the Project-owned default landing message:
curl -X POST \
'https://api.limelink.org/api/v2/organizations/22222222-2222-4222-8222-222222222222/projects' \
-H "X-API-KEY: $LIMELINK_API_KEY" \
-H 'Content-Type: application/json' \
--data '{"project_name":"Example Project","default_hookup_msg":"Open this content in the app"}'
Create a default-domain Link through the Core Link endpoint. Omit dynamic_link_suffix if the server should generate it:
curl -X POST 'https://api.limelink.org/api/v2/core/link' \
-H "X-API-KEY: $LIMELINK_API_KEY" \
-H 'Content-Type: application/json' \
--data '{
"project_id":"33333333-3333-4333-8333-333333333333",
"dynamic_link_name":"Spring campaign",
"dynamic_link_url":"https://destination.example/products/spring",
"stats_flag":true
}'
Resolve a full canonical short URL without parsing its suffix locally:
curl --get 'https://api.limelink.org/api/v2/links/resolve' \
-H "X-API-KEY: $LIMELINK_API_KEY" \
--data-urlencode 'url=https://example.limelink.org/campaign'
Swagger remains authoritative if a request field differs from these minimal recipes.
A common automation flow is:
- List or create a Project. Project create/update may set
default_hookup_msg, the Project-owned default landing message. - Create the iOS or Android Application records required by Link routing.
- Optionally create a Custom Domain and wait for its lifecycle to become active.
- Create a default-domain or Custom Domain Link.
- Use list and get operations to reconcile local state.
Lists that expose next_cursor use opaque cursor pagination; pass the returned cursor unchanged. Resolve an existing Link from its full canonical short URL with GET /api/v2/links/resolve; send the required url query value unchanged rather than parsing a suffix locally. Link readiness is represented by nullable short_url: non-null is ready, while null is not ready yet.
Core Link creation can infer compatible active platform Applications. Do not invent platform identifiers; follow the current Swagger request contract. The availability result is advisory and does not reserve a suffix—creation remains authoritative.
Custom Domain and error handling
New Custom Domains and Custom Domain Links require the owning Project's active Pro entitlement and valid domain lifecycle state. Default-domain Links remain available on Free. Provisioning and retry are asynchronous workflows; read the returned lifecycle fields rather than assuming immediate activation.
Handle failures by status and the current Swagger error schema:
400: invalid request or resource relationship401: missing, empty, unknown, or revoked API credential403: blocked Organization, missing scope, operation forbidden, or unmet paid-domain rule404: unavailable or outside-Organization resource409: lifecycle, uniqueness, dependency, or concurrent-state conflict502/503: documented Custom Domain provider or service failures
Use timeouts and bounded retries, validate user-controlled input, and avoid logging secrets or sensitive payloads. Swagger is reference-only: it does not persist authorization and interactive request submission is disabled.
Stats API boundary
Public SDK and landing-page Stats endpoints are separate from the Organization API-key contract above. V2 event ingestion accepts landing-page browser events without disclosing whether unknown, cross-Project, inactive, blocked, deleted, malformed, or ambiguous Link input matched a resource. Daily V2 stats reads are first-party bearer-authenticated console APIs. Do not use Organization API credentials for Stats routes unless Swagger explicitly lists that operation.