{
 "openapi": "3.1.0",
 "info": {
  "title": "Postcard API",
  "version": "1.0.0",
  "description": "Postcard checks the card a link shows when it is shared: it fetches the page like a link crawler, reads the og: and twitter: tags with X's fallbacks, downloads the share image to read its real size, format and bytes, checks robots.txt for the crawlers, and returns the card X, Facebook, LinkedIn…",
  "contact": {
   "name": "Clawhard Studio",
   "url": "https://clawhard.com",
   "email": "support@clawhard.com"
  }
 },
 "servers": [
  {
   "url": "https://postcard.clawhard.com"
  }
 ],
 "paths": {
  "/api/v1/check_link_preview": {
   "post": {
    "operationId": "check_link_preview",
    "summary": "Check the card a link shows when shared on X, Facebook, LinkedIn and chat apps",
    "description": "Use when the user asks how a link will look when shared, why a shared link shows no image or the wrong title, or wants a page checked before a launch post (signals: \"link preview\", \"og:image not showing\", \"Twitter card\", \"X card\", \"social card\", \"share image\", \"unfurl\", \"Open Graph\", \"LinkedIn…",
    "x-full-description": "Use when the user asks how a link will look when shared, why a shared link shows no image or the wrong title, or wants a page checked before a launch post (signals: \"link preview\", \"og:image not showing\", \"Twitter card\", \"X card\", \"social card\", \"share image\", \"unfurl\", \"Open Graph\", \"LinkedIn preview is blank\", \"after deploying the landing page\"). Fetches the public page the way a link crawler does (following redirects, no JavaScript), reads the og: and twitter: tags with X's documented fallbacks (twitter:* then og:*, last twitter:card wins), downloads the og:image and twitter:image and reads their real pixel size, format and file size from the bytes, and checks robots.txt for Twitterbot and facebookexternalhit on the page and the image. Returns the card X, Facebook, LinkedIn and chat apps will build, every problem with severity and fix (relative or http image URL, image that is really an HTML page, SVG or AVIF, below X's 300x157 / Facebook's 200x200 / LinkedIn's 1200x627, over 5 MB, 2:1 crop of a square logo, tags added by JavaScript, tags past Facebook's first 1 MB, title over X's 70 characters, missing twitter:card, robots.txt blocks), and a paste-ready <meta> block. Rules come from the platforms' docs, with the check date. Example: {\"url\":\"https://example.com/launch\"}. Limits: public http/https pages on ports 80 and 443 (localhost and unpublished pages go through postcard_check_preview_tags); platforms cache cards (X for about 7 days), so a fixed page can still show the old card until rescraped; sites that serve different HTML to real crawlers by User-Agent can differ. Returns: {summary, verdict: ready|needs_fixes|broken|blocked|unreachable, url, final_url, status, redirects, cards:{x:{layout, title, title_from, description, description_from, image:{url,width,height,loads}|null, image_from, domain}, facebook:{title, title_from, description, description_from, image, domain}, linkedin:{title, image, domain}, chat:{site_name, title, description, image, theme_color, note}}, image:{url, final_url, status, content_type, bytes, format, width, height, robots}|null, x_image?, problems:[{severity: error|warn|info, code, platforms, message, fix}], platforms_ok:{x, facebook, linkedin}, tags_found:[{key, attr, value}], fix_tags (paste-ready <meta> block, or null), robots:{page:{twitterbot, facebookexternalhit}}, page:{content_type, bytes, title, lang, js_shell}, rules:{checked, x_large_image, x_summary, x_text, facebook, linkedin, sources}, notes, mode, engine, checked_at, open_url}",
    "x-openai-isConsequential": false,
    "responses": {
     "200": {
      "description": "Result. Always has \"ok\": true.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad input. {\"ok\":false,\"error\":\"...\"}"
     },
     "429": {
      "description": "Rate limited. Retry after the Retry-After header."
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "url"
        ],
        "properties": {
         "url": {
          "type": "string",
          "minLength": 4,
          "maxLength": 2000,
          "description": "The page address people will share, for example \"https://example.com/blog/launch\"."
         }
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/check_preview_tags": {
   "post": {
    "operationId": "check_preview_tags",
    "summary": "Check share-card tags in pasted HTML (before deploy, localhost, private pages)",
    "description": "Use when the share-card tags need checking in HTML that is not public yet: a page being built, a localhost or staging page, a template, or a <head> block the user pasted (signals: \"check my meta tags\", \"are these og tags right\", \"before I deploy\", \"Next.js metadata\", \"index.html head\").",
    "x-full-description": "Use when the share-card tags need checking in HTML that is not public yet: a page being built, a localhost or staging page, a template, or a <head> block the user pasted (signals: \"check my meta tags\", \"are these og tags right\", \"before I deploy\", \"Next.js metadata\", \"index.html head\"). Reads the og: and twitter: tags in the pasted HTML with X's documented fallbacks and returns the same result as postcard_check_link_preview: the card each platform will build, problems with fixes (missing og:image or twitter:card, relative image path, title over X's 70 characters, duplicates, name= instead of property=, tags in <body>, a JavaScript app shell with no tags) and a paste-ready <meta> block. The page is not fetched; with fetch_image true, a public absolute image URL is downloaded to read its real size, format and bytes. Example: {\"html\":\"<head><meta property=\\\"og:title\\\" content=\\\"Launch\\\"><meta property=\\\"og:image\\\" content=\\\"/og.png\\\"></head>\",\"url\":\"https://example.com/launch\"}. Limits: robots.txt and redirects of the page are not checked (no fetch); without url, relative image paths cannot be resolved. Returns: {summary, verdict: ready|needs_fixes|broken|blocked|unreachable, url, final_url, status, redirects, cards:{x:{layout, title, title_from, description, description_from, image:{url,width,height,loads}|null, image_from, domain}, facebook:{title, title_from, description, description_from, image, domain}, linkedin:{title, image, domain}, chat:{site_name, title, description, image, theme_color, note}}, image:{url, final_url, status, content_type, bytes, format, width, height, robots}|null, x_image?, problems:[{severity: error|warn|info, code, platforms, message, fix}], platforms_ok:{x, facebook, linkedin}, tags_found:[{key, attr, value}], fix_tags (paste-ready <meta> block, or null), robots:{page:{twitterbot, facebookexternalhit}}, page:{content_type, bytes, title, lang, js_shell}, rules:{checked, x_large_image, x_summary, x_text, facebook, linkedin, sources}, notes, mode, engine, checked_at, open_url}",
    "x-openai-isConsequential": false,
    "responses": {
     "200": {
      "description": "Result. Always has \"ok\": true.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean"
          }
         },
         "additionalProperties": true
        }
       }
      }
     },
     "400": {
      "description": "Bad input. {\"ok\":false,\"error\":\"...\"}"
     },
     "429": {
      "description": "Rate limited. Retry after the Retry-After header."
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "html"
        ],
        "properties": {
         "html": {
          "type": "string",
          "minLength": 1,
          "maxLength": 400000,
          "description": "The page HTML, or at least its <head> with the meta tags (up to 400,000 characters)."
         },
         "url": {
          "type": "string",
          "minLength": 4,
          "maxLength": 2000,
          "description": "Optional address the page will be served at, used to turn relative image paths into full URLs and to compare og:url."
         },
         "fetch_image": {
          "type": "boolean",
          "description": "true to download the public og:image / twitter:image and read its real pixel size, format and file size. Default false (nothing is fetched)."
         }
        }
       }
      }
     }
    }
   }
  }
 },
 "externalDocs": {
  "description": "llms.txt",
  "url": "https://postcard.clawhard.com/llms.txt"
 }
}