Endpoints
All endpoints share the base path:
https://api.codelloy.com/link/external/v1Authentication is by X-API-KEY on every request. See Obtaining Your API Key to generate one and Mobile Apps for the deep-link setup that the schema below references via mobileAppId.
Every response is wrapped in the standard envelope { response: …, errors: … } — on success response is the payload and errors is omitted; on failure response is omitted and errors is a non-empty array. See API Errors for the full error catalogue.
Create or Update a Short URL
Creates a new short URL or updates an existing one (matched by shortCode).
POST /link/external/v1/shortenedUrl/saveRequest Headers
| Header | Value | Required |
|---|---|---|
X-API-KEY | Your API key | Yes |
Content-Type | application/json | Yes |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Destination URL. Must be non-empty. |
shortCode | string | No | Custom alias for the short link. 7–50 characters, alphanumeric / hyphen / underscore only (regex ^[a-zA-Z0-9_-]{7,50}$). Auto-generated when omitted. Re-using an existing shortCode belonging to your organisation updates that record. |
shortLinkName | string | No | Human-readable label shown in the dashboard list view. Strongly recommended for API-created links so operators can find them. |
isActive | boolean | No | Defaults to true. Set to false to archive the link without deleting it (clients get HTTP 410 EN002 at click time). |
utmSource | string | No | UTM source tag (appended to the destination URL at click time). |
utmCampaign | string | No | UTM campaign tag. |
utmMedium | string | No | UTM medium tag. |
enableCustomMetadata | boolean | No | When true, the customMetadata* fields below override the destination URL’s Open Graph preview for social-sharing surfaces. Requires the Metadata Preview feature on your plan. |
customMetadataTitle | string | No | Override title for link previews. |
customMetadataDescription | string | No | Override description for link previews. |
customMetadataImageUrl | string | No | Override image URL for link previews. |
deepLinks | array | No | Array of DeepLink objects (see sub-table below). Omit or pass [] for a plain redirect-only short URL. |
DeepLink Entry Schema
Each entry in the deepLinks[] array follows this shape.
| Field | Type | Direction | Description |
|---|---|---|---|
mobileAppId | string | Required on request | The 12-character ID from your Manage Apps dashboard. Identifies the registered MobileApp this entry targets. Null/blank/unknown values return DL001. |
deeplinkUrl | string | Request | Custom-scheme URI used when the app is installed (e.g. myapp://product/123). |
fallbackUrl | string | Request | Web URL used when fallbackEnabled=true and the app is not installed. |
linkBehaviour | string | Request | "app" (try the deep link first) or "webBrowser" (skip the app and go straight to url). |
fallbackEnabled | boolean | Request | When true, prefer fallbackUrl over the registered Store URL on app-not-installed. When false, prefer the Store URL. |
platform | string | Response only — server-controlled | Reflects the registered MobileApp.platform ("IOS" / "ANDROID" / "WEB"). Values supplied in the request body are silently overwritten on save. |
storeUrl | string | Response only — server-controlled | Reflects the registered MobileApp.storeUrl at read time. Edits via Manage Apps are picked up immediately on the next read. |
Example Request
curl -X POST https://api.codelloy.com/link/external/v1/shortenedUrl/save \
-H "X-API-KEY: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/product/123",
"shortCode": "spring-sale",
"shortLinkName": "Spring sale — product 123",
"deepLinks": [
{
"mobileAppId": "abc123def456",
"deeplinkUrl": "myapp://product/123",
"fallbackUrl": "https://example.com/product/123",
"linkBehaviour": "app",
"fallbackEnabled": true
},
{
"mobileAppId": "789xyz456ghi",
"deeplinkUrl": "myapp://product/123",
"fallbackUrl": "https://example.com/product/123",
"linkBehaviour": "app",
"fallbackEnabled": true
}
]
}'Success Response
HTTP/1.1 200 OK
Content-Type: application/json{
"response": {
"url": "https://example.com/product/123",
"shortCode": "spring-sale",
"shortLinkName": "Spring sale — product 123",
"smartLink": "https://re.codelloy.com/spring-sale",
"isActive": true,
"deepLinks": [
{
"mobileAppId": "abc123def456",
"platform": "IOS",
"storeUrl": "https://apps.apple.com/app/myapp/id123456789",
"deeplinkUrl": "myapp://product/123",
"fallbackUrl": "https://example.com/product/123",
"linkBehaviour": "app",
"fallbackEnabled": true,
"appId": 42
},
{
"mobileAppId": "789xyz456ghi",
"platform": "ANDROID",
"storeUrl": "https://play.google.com/store/apps/details?id=com.example.myapp",
"deeplinkUrl": "myapp://product/123",
"fallbackUrl": "https://example.com/product/123",
"linkBehaviour": "app",
"fallbackEnabled": true,
"appId": 43
}
],
"createdAt": "2026-05-15T10:30:00",
"updatedAt": "2026-05-15T10:30:00",
"createdBy": "api-key:Production Integration",
"updatedBy": "api-key:Production Integration",
"createdVia": "EXTERNAL",
"lastModifiedVia": "EXTERNAL"
}
}Custom domains:
smartLinkreflects your organisation’s active custom domain when the link is created (e.g.https://go.yourbrand.com/spring-sale). If the custom domain is later removed,https://re.codelloy.com/{shortCode}continues to resolve as a fallback.
Audit attribution:
createdByandupdatedBystart withapi-key:for any write performed through the External API — followed by the label you set on the key (or the default<orgName> API keyif you didn’t supply one). See Obtaining Your API Key for the labelling flow.
Validation Errors
A missing required field returns HTTP 400. Bean Validation errors emit SML002; ad-hoc validation (e.g. invalid shortCode format) emits VE001. Branch on the code field, not the HTTP status.
HTTP/1.1 400 Bad Request
Content-Type: application/json{
"errors": [
{
"code": "SML002",
"message": "must not be empty"
}
]
}A deepLinks[] entry with a missing or unknown mobileAppId returns HTTP 400 with DL001:
{
"errors": [
{
"code": "DL001",
"message": "Deep link entry rejected: mobileAppId 'unknown123' is not recognised for this organisation",
"details": {
"kind": "deep_link_validation",
"mobileAppId": "unknown123"
}
}
]
}Bulk Create or Update Short URLs
Creates or updates up to 30 short URLs in a single request.
POST /link/external/v1/shortenedUrl/bulkSaveRequest Headers
| Header | Value | Required |
|---|---|---|
X-API-KEY | Your API key | Yes |
Content-Type | application/json | Yes |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
data | array | Yes | Array of short-URL objects (same fields as the single save endpoint). Max 30 items. |
Example Request
curl -X POST https://api.codelloy.com/link/external/v1/shortenedUrl/bulkSave \
-H "X-API-KEY: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"data": [
{ "url": "https://example.com/page-one", "shortCode": "page-one", "shortLinkName": "Page One" },
{ "url": "https://example.com/page-two", "shortLinkName": "Page Two" }
]
}'Success Response
A single envelope wraps two arrays — successRecords (saved) and failureRecords (rejected) — plus a bulkSaveSummary count.
HTTP/1.1 200 OK
Content-Type: application/json{
"response": {
"successRecords": [
{
"shortCode": "page-one",
"url": "https://example.com/page-one",
"shortLinkName": "Page One",
"smartLink": "https://re.codelloy.com/page-one",
"isActive": true,
"createdVia": "EXTERNAL",
"lastModifiedVia": "EXTERNAL"
},
{
"shortCode": "a81bb82",
"url": "https://example.com/page-two",
"shortLinkName": "Page Two",
"smartLink": "https://re.codelloy.com/a81bb82",
"isActive": true,
"createdVia": "EXTERNAL",
"lastModifiedVia": "EXTERNAL"
}
],
"failureRecords": [],
"bulkSaveSummary": {
"successCount": 2,
"failureCount": 0
}
}
}Whole-batch validation failure
If the outer payload is malformed (e.g. data missing or empty), the entire request fails with the standard errors[] envelope — no per-record processing happens.
{
"errors": [
{ "code": "SML002", "message": "must not be empty" }
]
}List Short URLs
Returns a paginated list of your short URLs. All filtering is via the query DSL parameter (described below).
GET /link/external/v1/shortenedUrl?page={page}&size={size}&query={query}Request Headers
| Header | Value | Required |
|---|---|---|
X-API-KEY | Your API key | Yes |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 0 | Page number (zero-based). |
size | integer | 20 | Number of results per page. |
query | string | — | Filter expression in the Codelloy query DSL (see below). |
Example Request
curl -X GET "https://api.codelloy.com/link/external/v1/shortenedUrl?page=0&size=10&query=isActive=true" \
-H "X-API-KEY: your_api_key_here"Success Response
The response envelope wraps a data[] array of short-URL objects plus a page metadata object with size / number / totalElements / totalPages.
HTTP/1.1 200 OK
Content-Type: application/json{
"response": {
"data": [
{
"url": "https://example.com/product/123",
"shortCode": "spring-sale",
"shortLinkName": "Spring sale — product 123",
"smartLink": "https://re.codelloy.com/spring-sale",
"isActive": true,
"enableCustomMetadata": false,
"createdAt": "2026-05-15T10:30:00.742912",
"updatedAt": "2026-05-15T10:30:00.742912",
"createdBy": "api-key:Production Integration",
"updatedBy": "api-key:Production Integration",
"createdVia": "EXTERNAL",
"lastModifiedVia": "EXTERNAL"
}
],
"page": {
"size": 10,
"number": 0,
"totalElements": 42,
"totalPages": 5
}
}
}
createdAt/updatedAtare serialised as JavaLocalDateTime— no trailingZ(no timezone) and microsecond precision. Parse in your client with aLocalDateTime/naive datetimestrategy, not an instant-with-zone parser.
Query DSL — Filtering Syntax
The query parameter accepts structured expressions in the form field=value. Multiple expressions can be combined with AND / OR.
| Field | Type | Example |
|---|---|---|
url | string | url=https://example.com/page |
shortCode | string | shortCode=spring-sale |
shortLinkName | string | shortLinkName=Spring sale |
smartLink | string | smartLink=https://re.codelloy.com/abc |
isActive | boolean | isActive=true |
enableCustomMetadata | boolean | enableCustomMetadata=true |
createdBy | string | createdBy=alice@example.com or createdBy=api-key:Production Integration |
updatedBy | string | Same shape as createdBy. |
createdVia | enum | createdVia=EXTERNAL or createdVia=INTERNAL (case-insensitive). Filter for rows created via the External API or the dashboard. |
lastModifiedVia | enum | Same shape as createdVia — match the most recent write source. |
Combine expressions with AND (intersection) or OR (union). Whitespace around operators is optional.
# All EXTERNAL-created links that are still active
curl -X GET "https://api.codelloy.com/link/external/v1/shortenedUrl?query=createdVia=EXTERNAL AND isActive=true" \
-H "X-API-KEY: your_api_key_here"Unknown enum values return HTTP 400 VE001 with a “Invalid value ‘X’ for enum ChangeSource” message; valid values are INTERNAL and EXTERNAL.