{
  "openapi": "3.1.0",
  "info": {
    "title": "skrat.im API",
    "version": "1.0.0",
    "summary": "URL shortener with custom aliases, QR codes and cookie-free click statistics.",
    "description": "Shorten URLs on skrat.im or krat.im. Anonymous requests are rate-limited per IP (60/hour, 300/day). With an API key (Authorization: Bearer skrat_…) links are saved to your account and the limit is 1000/hour. Anonymous links return an edit_token once; keep it to edit or delete the link later. An MCP server for AI agents is available at /mcp.",
    "contact": {
      "name": "skrat.im",
      "url": "https://skrat.im/en/about/",
      "email": "info@skrat.im"
    },
    "termsOfService": "https://skrat.im/en/terms/"
  },
  "servers": [
    {
      "url": "https://skrat.im"
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key from /app/settings/ (prefix skrat_)."
      },
      "editToken": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Edit-Token",
        "description": "Edit token returned when an anonymous link was created."
      }
    },
    "schemas": {
      "Link": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "domain": {
            "type": "string",
            "enum": [
              "skrat.im",
              "krat.im"
            ]
          },
          "slug": {
            "type": "string"
          },
          "short_url": {
            "type": "string",
            "format": "uri",
            "example": "https://skrat.im/abc123"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "expired",
              "disabled"
            ]
          },
          "clicks": {
            "type": "integer"
          },
          "has_password": {
            "type": "boolean"
          },
          "public_stats": {
            "type": "boolean"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "max_clicks": {
            "type": [
              "integer",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_click_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "preview_url": {
            "type": "string",
            "format": "uri",
            "description": "Short URL with a trailing +; shows the destination without redirecting."
          },
          "qr_svg_url": {
            "type": "string",
            "format": "uri"
          },
          "qr_png_url": {
            "type": "string",
            "format": "uri"
          },
          "stats_url": {
            "type": "string",
            "format": "uri"
          },
          "edit_token": {
            "type": "string",
            "description": "Only for anonymous links, returned once on creation. Send as X-Edit-Token to edit or delete."
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        }
      },
      "CreateLink": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "description": "Long URL (http/https). A missing scheme defaults to https://."
          },
          "alias": {
            "type": "string",
            "description": "Custom alias, 2-40 chars: a-z 0-9 - _. Random when omitted."
          },
          "domain": {
            "type": "string",
            "enum": [
              "skrat.im",
              "krat.im"
            ],
            "default": "skrat.im"
          },
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "expires_in_days": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 3650
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "max_clicks": {
            "type": "integer",
            "minimum": 1
          },
          "password": {
            "type": "string",
            "minLength": 4,
            "maxLength": 64
          },
          "public_stats": {
            "type": "boolean",
            "default": true
          },
          "utm_source": {
            "type": "string"
          },
          "utm_medium": {
            "type": "string"
          },
          "utm_campaign": {
            "type": "string"
          },
          "utm_term": {
            "type": "string"
          },
          "utm_content": {
            "type": "string"
          }
        }
      }
    }
  },
  "paths": {
    "/api/v1/links": {
      "post": {
        "summary": "Create a short link",
        "operationId": "createLink",
        "security": [
          {},
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateLink"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Link"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (Retry-After header)"
          }
        }
      },
      "get": {
        "summary": "List links of the authenticated account",
        "operationId": "listLinks",
        "security": [
          {
            "apiKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "links": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Link"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/links/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Link detail",
        "operationId": "getLink",
        "security": [
          {
            "apiKey": []
          },
          {
            "editToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Link"
                }
              }
            }
          },
          "403": {
            "description": "No access"
          },
          "404": {
            "description": "Not found"
          }
        }
      },
      "patch": {
        "summary": "Edit a link",
        "operationId": "updateLink",
        "security": [
          {
            "apiKey": []
          },
          {
            "editToken": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "title": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "expires_in_days": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "null removes the expiry"
                  },
                  "expires_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  },
                  "max_clicks": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "password": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "empty string or null removes the password"
                  },
                  "public_stats": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Link"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a link",
        "operationId": "deleteLink",
        "security": [
          {
            "apiKey": []
          },
          {
            "editToken": []
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          }
        }
      }
    },
    "/api/v1/links/{id}/stats": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Click statistics",
        "operationId": "linkStats",
        "description": "Public when the owner keeps statistics public (default); otherwise requires the API key or edit token.",
        "security": [
          {},
          {
            "apiKey": []
          },
          {
            "editToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "unique": {
                      "type": "integer",
                      "description": "Unique visitors in the last 30 days (daily-salted hash, no IP stored)."
                    },
                    "bots": {
                      "type": "integer"
                    },
                    "days": {
                      "type": "integer"
                    },
                    "by_day": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string",
                            "format": "date"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "countries": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "referrers": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "devices": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "browsers": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "key": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/check": {
      "get": {
        "summary": "Check alias availability",
        "operationId": "checkAlias",
        "parameters": [
          {
            "name": "slug",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "domain",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "skrat.im",
                "krat.im"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "slug": {
                      "type": "string"
                    },
                    "domain": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "free",
                        "taken",
                        "reserved",
                        "invalid"
                      ]
                    },
                    "alternative": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Other domain where the alias is free."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/bulk": {
      "post": {
        "summary": "Shorten many URLs at once",
        "operationId": "bulkCreate",
        "description": "Up to 20 URLs anonymously, 200 with an API key.",
        "security": [
          {},
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "urls"
                ],
                "properties": {
                  "urls": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      {
                        "type": "string",
                        "description": "one URL per line"
                      }
                    ]
                  },
                  "domain": {
                    "type": "string",
                    "enum": [
                      "skrat.im",
                      "krat.im"
                    ]
                  },
                  "expires_in_days": {
                    "type": "number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Per-URL results in input order"
          }
        }
      }
    },
    "/api/v1/links/claim": {
      "post": {
        "summary": "Attach anonymous links (by edit tokens) to the account",
        "operationId": "claimLinks",
        "security": [
          {
            "apiKey": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "edit_tokens": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "{ claimed: n }"
          }
        }
      }
    },
    "/api/shorten": {
      "get": {
        "summary": "Plain-text shortener for shells and scripts",
        "operationId": "shortenText",
        "description": "Returns only the short URL as text/plain. Anonymous links get the edit token in the X-Edit-Token response header.",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "alias",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "domain",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "skrat.im",
                "krat.im"
              ]
            }
          }
        ],
        "responses": {
          "201": {
            "description": "The short URL",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "example": "https://skrat.im/abc123"
                }
              }
            }
          }
        }
      }
    },
    "/{slug}+": {
      "get": {
        "summary": "Preview page of a short link (HTML)",
        "operationId": "previewLink",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Destination, creation date and public stats"
          }
        }
      }
    },
    "/{slug}.json": {
      "get": {
        "summary": "Public JSON info about a short link",
        "operationId": "linkJson",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "short_url, url (unless password protected), status, created_at, clicks (if public)"
          }
        }
      }
    },
    "/{slug}.png": {
      "get": {
        "summary": "QR code PNG",
        "operationId": "qrPng",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "size",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 64,
              "maximum": 2048,
              "default": 512
            }
          },
          {
            "name": "fg",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "#000000"
            }
          },
          {
            "name": "bg",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "#ffffff"
            }
          },
          {
            "name": "margin",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 8
            }
          }
        ],
        "responses": {
          "200": {
            "description": "image/png"
          }
        }
      }
    },
    "/{slug}.svg": {
      "get": {
        "summary": "QR code SVG",
        "operationId": "qrSvg",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "fg",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "bg",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "image/svg+xml"
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "summary": "MCP server (Streamable HTTP, JSON-RPC 2.0)",
        "operationId": "mcp",
        "description": "Tools: shorten_url, check_alias, get_link_info, qr_code. Stateless; send initialize, tools/list, tools/call.",
        "responses": {
          "200": {
            "description": "JSON-RPC response"
          }
        }
      }
    }
  }
}