{
  "openapi": "3.1.0",
  "info": {
    "title": "URLMe API",
    "version": "1.0.0",
    "description": "Create and manage short links. Custom aliases are available only on active custom domains for Growth and Scale workspaces.",
    "contact": { "name": "URLMe Support", "email": "support@urlme.link", "url": "https://www.urlme.link/contact" }
  },
  "servers": [{ "url": "https://api.urlme.link", "description": "Production" }],
  "security": [{ "bearerAuth": [] }],
  "tags": [
    { "name": "Links", "description": "Short-link creation and lifecycle" },
    { "name": "Account", "description": "Workspace usage and service state" }
  ],
  "paths": {
    "/v1/links": {
      "post": {
        "tags": ["Links"],
        "operationId": "createLink",
        "summary": "Create a short link",
        "description": "Creates an immutable short link. Creates an automatic code by default. customCode requires domain, an active connected custom domain, and a Growth or Scale plan.",
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateLinkRequest" }, "examples": {
            "automatic": { "summary": "Automatic URLMe code", "value": { "url": "https://example.com/campaign/welcome", "description": "Welcome campaign", "expiresAt": "2026-12-31T23:59:59Z" } },
            "customAlias": { "summary": "Custom alias on a custom domain", "value": { "url": "https://example.com/onboarding", "description": "Customer onboarding", "domain": "go.example.com", "customCode": "welcome" } }
          } } }
        },
        "responses": {
          "201": { "description": "Link created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LinkResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/ServiceInactive" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "409": { "$ref": "#/components/responses/Conflict" },
          "422": { "$ref": "#/components/responses/UnsafeDestination" },
          "429": { "$ref": "#/components/responses/LimitReached" }
        }
      },
      "get": {
        "tags": ["Links"],
        "operationId": "listLinks",
        "summary": "List newest links",
        "description": "Returns newest-first, non-deleted links. Cursor pagination is not currently exposed.",
        "parameters": [{ "name": "limit", "in": "query", "required": false, "description": "Defaults to 50 and is clamped from 1 to 100.", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 } }],
        "responses": {
          "200": { "description": "Links", "content": { "application/json": { "schema": { "type": "object", "required": ["data"], "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/StoredLink" } } } } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/v1/links/bulk": {
      "post": {
        "tags": ["Links"], "operationId": "createLinksBulk", "summary": "Create links in a capped batch",
        "description": "Creates each item through the same quota, safety, domain, and expiration controls as single creation. Returns 207 when only some items succeed.",
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["links"], "properties": { "links": { "type": "array", "minItems": 1, "maxItems": 250, "items": { "$ref": "#/components/schemas/CreateLinkRequest" } } } } } } },
        "responses": { "201": { "description": "All links created" }, "207": { "description": "Partial success" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "413": { "description": "Plan batch limit exceeded" }, "429": { "$ref": "#/components/responses/LimitReached" } }
      }
    },
    "/v1/links/{id}": {
      "get": {
        "tags": ["Links"], "operationId": "getLink", "summary": "Get link details and analytics",
        "parameters": [{ "$ref": "#/components/parameters/LinkId" }],
        "responses": { "200": { "description": "Link details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LinkResponse" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" } }
      },
      "patch": {
        "tags": ["Links"], "operationId": "updateLink", "summary": "Update a link",
        "description": "Updates private metadata or active/paused state. The destination, hostname, and code can never be changed; create a new link instead.",
        "parameters": [{ "$ref": "#/components/parameters/LinkId" }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateLinkRequest" }, "examples": { "label": { "summary": "Set an internal description", "value": { "description": "Summer campaign" } }, "clearLabel": { "summary": "Clear the description", "value": { "description": null } }, "pause": { "summary": "Pause redirects", "value": { "status": "paused" } } } } } },
        "responses": {
          "200": { "description": "Link updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LinkUpdateResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "402": { "$ref": "#/components/responses/ServiceInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "409": { "$ref": "#/components/responses/Conflict" }
        }
      },
      "delete": {
        "tags": ["Links"], "operationId": "deleteLink", "summary": "Delete a link", "description": "Removes the redirect record. The response has no body.",
        "parameters": [{ "$ref": "#/components/parameters/LinkId" }],
        "responses": { "204": { "description": "Deleted" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "402": { "$ref": "#/components/responses/ServiceInactive" }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },
    "/v1/links/{id}/expire": {
      "post": {
        "tags": ["Links"], "operationId": "expireLink", "summary": "Permanently expire a link", "description": "Irreversible. Future redirects return HTTP 410.",
        "parameters": [{ "$ref": "#/components/parameters/LinkId" }],
        "responses": {
          "200": { "description": "Expired", "content": { "application/json": { "schema": { "type": "object", "required": ["data"], "properties": { "data": { "type": "object", "required": ["id", "status", "expiresAt"], "properties": { "id": { "type": "string" }, "status": { "const": "expired" }, "expiresAt": { "type": "string", "format": "date-time" } } } } } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }, "402": { "$ref": "#/components/responses/ServiceInactive" }, "404": { "$ref": "#/components/responses/NotFound" }, "409": { "$ref": "#/components/responses/Conflict" }
        }
      }
    },
    "/v1/account": {
      "get": {
        "tags": ["Account"], "operationId": "getAccount", "summary": "Read workspace usage and service state",
        "responses": {
          "200": { "description": "Account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AccountResponse" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": { "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "URLMe API key", "description": "Use an active urlme_live_ API key." } },
    "parameters": { "LinkId": { "name": "id", "in": "path", "required": true, "description": "URLMe link document ID, not the short code.", "schema": { "type": "string" } }, "IdempotencyKey": { "name": "Idempotency-Key", "in": "header", "required": false, "description": "Unique retry key, retained for 24 hours. Reusing a key with a different request returns 409.", "schema": { "type": "string", "minLength": 8, "maxLength": 128 } } },
    "schemas": {
      "CreateLinkRequest": {
        "type": "object", "additionalProperties": false, "required": ["url"],
        "properties": {
          "url": { "type": "string", "minLength": 8, "maxLength": 8192, "format": "uri", "description": "Public HTTP or HTTPS destination." },
          "description": { "type": "string", "maxLength": 160, "description": "Optional internal label returned by create and list operations. It is never shown during redirects." },
          "tags": { "type": "array", "maxItems": 10, "uniqueItems": true, "items": { "type": "string", "minLength": 1, "maxLength": 32 }, "description": "Optional private tags for filtering and exports." },
          "utm": { "type": "object", "additionalProperties": false, "minProperties": 1, "properties": { "source": { "type": "string", "maxLength": 100 }, "medium": { "type": "string", "maxLength": 100 }, "campaign": { "type": "string", "maxLength": 200 }, "term": { "type": "string", "maxLength": 200 }, "content": { "type": "string", "maxLength": 200 } }, "description": "Campaign parameters appended to the destination once at creation." },
          "expiresAt": { "type": "string", "format": "date-time", "description": "Optional future ISO 8601 expiration timestamp. Current or past timestamps return 400 expiration_must_be_future." },
          "domain": { "type": "string", "maxLength": 253, "description": "Active custom hostname owned by the workspace. Growth or Scale only." },
          "customCode": { "type": "string", "minLength": 4, "maxLength": 32, "pattern": "^[A-Za-z0-9_-]+$", "description": "Case-preserving custom alias. Requires domain and is unavailable on urlme.link." }
        }
      },
      "UpdateLinkRequest": {
        "type": "object", "additionalProperties": false, "minProperties": 1,
        "properties": {
          "status": { "type": "string", "enum": ["active", "paused"], "description": "Optional redirect-delivery state." },
          "description": { "type": ["string", "null"], "maxLength": 160, "description": "Optional private internal label. Send null or an empty string to clear it." },
          "tags": { "type": ["array", "null"], "maxItems": 10, "items": { "type": "string", "minLength": 1, "maxLength": 32 } }
        }
      },
      "Link": {
        "type": "object", "required": ["id", "code", "shortUrl", "destination", "description", "clicks", "status", "createdAt", "expiresAt"],
        "properties": {
          "id": { "type": "string" }, "code": { "type": "string" }, "shortUrl": { "type": "string", "format": "uri" }, "destination": { "type": "string", "format": "uri" }, "description": { "type": ["string", "null"], "maxLength": 160 }, "tags": { "type": "array", "items": { "type": "string" } }, "clicks": { "type": "integer", "minimum": 0 }, "status": { "type": "string", "enum": ["active", "paused", "expired"] }, "createdAt": { "type": ["string", "null"], "format": "date-time" }, "lastClickedAt": { "type": ["string", "null"], "format": "date-time" }, "expiresAt": { "type": ["string", "null"], "format": "date-time" }, "analytics": { "type": "object", "properties": { "devices": { "type": "object", "additionalProperties": { "type": "integer" } }, "referrers": { "type": "object", "additionalProperties": { "type": "integer" } } } }
        }
      },
      "StoredLink": { "allOf": [{ "$ref": "#/components/schemas/Link" }, { "type": "object", "required": ["host", "updatedAt"], "properties": { "host": { "type": "string" }, "updatedAt": { "type": ["string", "null"], "format": "date-time" } } }] },
      "LinkResponse": { "type": "object", "required": ["data"], "properties": { "data": { "$ref": "#/components/schemas/Link" } } },
      "LinkUpdateResponse": { "type": "object", "required": ["data"], "properties": { "data": { "type": "object", "required": ["id", "status", "description", "tags"], "properties": { "id": { "type": "string" }, "status": { "type": "string", "enum": ["active", "paused"] }, "description": { "type": ["string", "null"], "maxLength": 160 }, "tags": { "type": "array", "items": { "type": "string" } } } } } },
      "Error": { "type": "object", "required": ["error"], "properties": { "error": { "type": "object", "required": ["code", "message"], "properties": { "code": { "type": "string" }, "message": { "type": "string" } } } } },
      "AccountResponse": {
        "type": "object", "required": ["data"],
        "properties": { "data": { "type": "object", "description": "Workspace service, billing-window usage, plan limits, and resource counts.", "properties": {
          "id": { "type": "string" }, "name": { "type": "string" }, "billingEmail": { "type": "string", "format": "email" }, "plan": { "type": "string", "enum": ["trial", "starter", "growth", "scale"] }, "subscriptionStatus": { "type": "string" }, "accessStatus": { "type": "string" }, "serviceActive": { "type": "boolean" }, "usagePeriodType": { "type": "string" }, "usagePeriodStart": { "type": ["string", "null"], "format": "date-time" }, "usagePeriodEnd": { "type": ["string", "null"], "format": "date-time" }, "usage": { "type": "object", "properties": { "shortens": { "type": "integer" }, "redirects": { "type": "integer" } } }, "limits": { "type": "object", "properties": { "shortens": { "type": "integer" }, "redirects": { "type": "integer" }, "apiKeys": { "type": "integer" }, "customDomains": { "type": "integer" }, "bulkBatchSize": { "type": "integer" } } }
        } } }
      }
    },
    "responses": {
      "BadRequest": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Unauthorized": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": { "code": "authentication_required", "message": "Provide a Bearer token or API key." } } } } },
      "ServiceInactive": { "description": "Workspace service access is inactive", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Forbidden": { "description": "Plan or domain entitlement unavailable", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotFound": { "description": "Resource not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Conflict": { "description": "Resource state or alias conflict", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "UnsafeDestination": { "description": "Destination blocked by safety controls", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "LimitReached": { "description": "Plan usage limit reached", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    }
  }
}
