{
  "openapi": "3.1.0",
  "info": {
    "title": "Majal public web surface",
    "version": "1.0.0",
    "summary": "Publicly reachable resources of majal.link (link-in-bio profiles, digital business cards, blog) and its agent-discovery documents.",
    "description": "Majal (مجال) hosts personal and business link-in-bio profiles and digital business cards for Arabic- and English-speaking creators.\n\nEverything listed here is readable **without credentials** and is returned as HTML (profiles, cards, blog) or as a machine-readable document (catalog, OpenAPI, health).\nThere is **no public write API**. Creating or editing profiles, cards, orders and analytics requires a signed-in human account owner (session cookie obtained through the web login at `/login`).\nAutomated clients must not attempt the login, OTP or payment flows; see `/auth.md`.",
    "contact": {
      "name": "Majal",
      "url": "https://majal.link/contact",
      "email": "contact@majal.link"
    },
    "termsOfService": "https://majal.link/t&c"
  },
  "externalDocs": {
    "description": "Human-readable documentation",
    "url": "https://majal.link/docs/api"
  },
  "servers": [
    {
      "url": "https://majal.link",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "profiles",
      "description": "Public link-in-bio profiles and digital cards"
    },
    {
      "name": "blog",
      "description": "Majal blog articles (Arabic and English)"
    },
    {
      "name": "discovery",
      "description": "Machine-readable discovery documents for crawlers and agents"
    }
  ],
  "paths": {
    "/p/{page}": {
      "get": {
        "tags": [
          "profiles"
        ],
        "operationId": "getProfilePage",
        "summary": "Public profile page",
        "description": "Renders the public link-in-bio profile for the given profile slug. Paid-plan owners may also expose a short alias at `/{alias}` which redirects here.",
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "description": "Profile slug (the part after majal.link/p/).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "HTML page",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "description": "Rendered HTML page"
                }
              }
            }
          },
          "404": {
            "description": "Not found. An HTML error page is returned.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "description": "Rendered HTML page"
                }
              }
            }
          }
        }
      }
    },
    "/c/{cardId}": {
      "get": {
        "tags": [
          "profiles"
        ],
        "operationId": "getDigitalCard",
        "summary": "Digital business card page",
        "description": "Renders a digital business card (or, for cards configured as a plain redirect, responds with a 302 to the owner's website).",
        "parameters": [
          {
            "name": "cardId",
            "in": "path",
            "required": true,
            "description": "Card identifier printed on / encoded in the physical card.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "HTML page",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "description": "Rendered HTML page"
                }
              }
            }
          },
          "302": {
            "description": "Card is configured to redirect to an external website.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "404": {
            "description": "Not found. An HTML error page is returned.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "description": "Rendered HTML page"
                }
              }
            }
          }
        }
      }
    },
    "/{lang}/blog": {
      "get": {
        "tags": [
          "blog"
        ],
        "operationId": "listBlogArticles",
        "summary": "Blog index",
        "parameters": [
          {
            "name": "lang",
            "in": "path",
            "required": true,
            "description": "Content language. Omitting the segment entirely (e.g. `/blog`) serves the visitor's preferred language.",
            "schema": {
              "type": "string",
              "enum": [
                "ar",
                "en"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "HTML page",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "description": "Rendered HTML page"
                }
              }
            }
          }
        }
      }
    },
    "/{lang}/blog/{slug}": {
      "get": {
        "tags": [
          "blog"
        ],
        "operationId": "getBlogArticle",
        "summary": "Blog article",
        "parameters": [
          {
            "name": "lang",
            "in": "path",
            "required": true,
            "description": "Content language. Omitting the segment entirely (e.g. `/blog`) serves the visitor's preferred language.",
            "schema": {
              "type": "string",
              "enum": [
                "ar",
                "en"
              ]
            }
          },
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "HTML page",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "description": "Rendered HTML page"
                }
              }
            }
          },
          "404": {
            "description": "Not found. An HTML error page is returned.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "description": "Rendered HTML page"
                }
              }
            }
          }
        }
      }
    },
    "/sitemap.xml": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getSitemap",
        "summary": "XML sitemap of public pages and articles",
        "responses": {
          "200": {
            "description": "Sitemap",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/robots.txt": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getRobots",
        "summary": "Crawler access rules",
        "responses": {
          "200": {
            "description": "robots.txt",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/api-catalog": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getApiCatalog",
        "summary": "API catalog (RFC 9727)",
        "description": "Linkset (RFC 9264) pointing at this OpenAPI document, the documentation, the auth policy and the health endpoint.",
        "responses": {
          "200": {
            "description": "Linkset",
            "content": {
              "application/linkset+json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiCatalog"
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getOpenApi",
        "summary": "This OpenAPI document",
        "responses": {
          "200": {
            "description": "OpenAPI 3.1 document",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/docs/api": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getApiDocs",
        "summary": "Human-readable documentation (HTML). Append `.md` for the Markdown source.",
        "responses": {
          "200": {
            "description": "HTML page",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string",
                  "description": "Rendered HTML page"
                }
              }
            }
          }
        }
      }
    },
    "/auth.md": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getAuthMd",
        "summary": "Agent registration and credential policy (auth.md)",
        "responses": {
          "200": {
            "description": "Markdown document",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "post": {
        "tags": [
          "discovery"
        ],
        "operationId": "mcp",
        "summary": "MCP server (Streamable HTTP, stateless)",
        "description": "Model Context Protocol endpoint. Send JSON-RPC 2.0 requests (`initialize`, `tools/list`, `tools/call`, `resources/list`, `resources/read`) with `Accept: application/json, text/event-stream`. All tools are read-only and need no authentication. Capabilities are described at `/.well-known/mcp/server-card.json`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "JSON-RPC 2.0 request or batch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC 2.0 response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request (JSON-RPC error object)"
          },
          "406": {
            "description": "Missing `Accept: application/json, text/event-stream`"
          }
        }
      }
    },
    "/.well-known/mcp/server-card.json": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getMcpServerCard",
        "summary": "MCP Server Card (SEP-1649)",
        "description": "Describes the MCP server: identity, Streamable HTTP endpoint, supported protocol versions, capabilities and tools.",
        "responses": {
          "200": {
            "description": "Server Card",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/health": {
      "get": {
        "tags": [
          "discovery"
        ],
        "operationId": "getHealth",
        "summary": "Liveness / readiness status",
        "description": "Returns 200 when the application and its database connection are healthy, 503 otherwise. Not cached.",
        "responses": {
          "200": {
            "description": "Healthy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "503": {
            "description": "Degraded (database unavailable)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Health": {
        "type": "object",
        "required": [
          "status",
          "service",
          "time"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "degraded"
            ]
          },
          "service": {
            "type": "string",
            "const": "majal"
          },
          "time": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ApiCatalog": {
        "type": "object",
        "required": [
          "linkset"
        ],
        "properties": {
          "linkset": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "anchor"
              ],
              "properties": {
                "anchor": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "additionalProperties": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "href"
                  ],
                  "properties": {
                    "href": {
                      "type": "string",
                      "format": "uri"
                    },
                    "type": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "securitySchemes": {
      "ownerSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "token",
        "description": "Session cookie issued to a signed-in human account owner through the web login (`/login`: e-mail one-time code, Google or Facebook). It is not issued to automated clients and no endpoint in this document requires it."
      }
    }
  }
}