{
  "openapi": "3.1.0",
  "info": {
    "title": "Faro",
    "summary": "A trust API: one red/yellow/green verdict for URLs, payees, and messages.",
    "description": "\nFaro returns **one consistent verdict** \u2014 \ud83d\udfe2 green, \ud83d\udfe1 yellow, or \ud83d\udd34 red \u2014 for a\nURL, a payee, or a message, with a plain-language `reason` and a\n`recommended_action` (`allow` / `warn` / `block` / `review`).\n\nA verdict is computed in tiers: fast deterministic checks (Google Safe Browsing,\ndomain age via RDAP, sanctions screening, global payment-handle format) decide the\nclear cases, and an LLM reasons only about the genuinely ambiguous ones \u2014 so most\ncalls never touch the model and stay fast and cheap.\n\n**Auth.** Send your key as `Authorization: Bearer <key>`.\n**Caching.** Responses carry `X-Faro-Cache: hit|miss`; every response carries\n`X-Request-ID`.\n",
    "contact": {
      "name": "Faro",
      "url": "https://farofinance.app/"
    },
    "license": {
      "name": "Proprietary"
    },
    "version": "1.1.2"
  },
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "Liveness check",
        "description": "Returns `{\"status\": \"ok\"}` when the service is up. No auth required.",
        "operationId": "health_health_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "type": "string"
                  },
                  "type": "object",
                  "title": "Response Health Health Get"
                }
              }
            }
          }
        }
      }
    },
    "/v1/verify/url": {
      "post": {
        "tags": [
          "Verify"
        ],
        "summary": "Verify a URL",
        "description": "Check a link before visiting, sharing, or acting on it. Runs Google Safe Browsing and a domain-age (RDAP) check, and \u2014 only when those are inconclusive \u2014 an LLM that reasons about brand impersonation and deceptive intent.",
        "operationId": "verify_url_v1_verify_url_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ValueIn"
              },
              "examples": {
                "clean": {
                  "summary": "A well-known site",
                  "value": {
                    "value": "https://www.google.com"
                  }
                },
                "phishing": {
                  "summary": "A KYC-impersonation phishing link",
                  "value": {
                    "value": "http://sbi-kyc-update.xyz/verify"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The verdict for the artifact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerdictResult"
                },
                "examples": {
                  "green": {
                    "summary": "A clean, well-established link",
                    "value": {
                      "verdict": "green",
                      "confidence": 0.8,
                      "artifact": {
                        "type": "url",
                        "value": "https://www.google.com"
                      },
                      "reason": "No safety problems found for this link.",
                      "recommended_action": "allow",
                      "signals": [
                        {
                          "name": "safe_browsing_clean",
                          "severity": "info",
                          "detail": "No Google Safe Browsing match.",
                          "source": "safe_browsing"
                        },
                        {
                          "name": "domain_age_days",
                          "severity": "info",
                          "detail": "Domain has been registered for 9863 day(s).",
                          "source": "rdap"
                        }
                      ],
                      "request_id": "b1946ac92492d2347c6235b4d2611184",
                      "cached": false,
                      "first_seen": "2026-06-01T09:12:44Z",
                      "checked_at": "2026-06-19T11:30:05Z"
                    }
                  },
                  "red": {
                    "summary": "A confirmed phishing link",
                    "value": {
                      "verdict": "red",
                      "confidence": 0.97,
                      "artifact": {
                        "type": "url",
                        "value": "http://sbi-kyc-update.xyz/verify"
                      },
                      "reason": "Google Safe Browsing flags this URL as Social Engineering.",
                      "recommended_action": "block",
                      "signals": [
                        {
                          "name": "safe_browsing_threat",
                          "severity": "critical",
                          "detail": "Google Safe Browsing flags this URL as Social Engineering.",
                          "source": "safe_browsing"
                        },
                        {
                          "name": "domain_age_days",
                          "severity": "high",
                          "detail": "Domain was registered only 3 day(s) ago.",
                          "source": "rdap"
                        }
                      ],
                      "request_id": "3c59dc048e8850243be8079a5c74d079",
                      "cached": false,
                      "first_seen": "2026-06-19T11:31:10Z",
                      "checked_at": "2026-06-19T11:31:10Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": "unauthorized",
                  "message": "Invalid API key.",
                  "request_id": "b1946ac92492d2347c6235b4d2611184"
                }
              }
            }
          },
          "422": {
            "description": "Request body failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": "invalid_request",
                  "message": "Request body failed validation.",
                  "request_id": "b1946ac92492d2347c6235b4d2611184"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded \u2014 slow down.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": "rate_limited",
                  "message": "Too many requests. Slow down.",
                  "request_id": "b1946ac92492d2347c6235b4d2611184"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    },
    "/v1/verify/payee": {
      "post": {
        "tags": [
          "Verify"
        ],
        "summary": "Verify a payee",
        "description": "Check a payee before sending money. Detects the payment-handle format across global rails (UPI, Venmo, Cash App, Pix, IBAN, email/phone) for transparency, screens against sanctions and OffshoreLeaks, and enriches business names with GLEIF (LEI) and GDELT adverse media. **Honest note:** Faro does not confirm an account exists or belongs to anyone \u2014 it reads public signals and advises. Format detection is always informational.",
        "operationId": "verify_payee_v1_verify_payee_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ValueIn"
              },
              "examples": {
                "upi": {
                  "summary": "A UPI VPA (India)",
                  "value": {
                    "value": "ramesh@okaxis"
                  }
                },
                "venmo": {
                  "summary": "A Venmo username (US)",
                  "value": {
                    "value": "@jane-doe-99"
                  }
                },
                "cashapp": {
                  "summary": "A Cash App $cashtag (US)",
                  "value": {
                    "value": "$JaneDoe"
                  }
                },
                "sanctioned": {
                  "summary": "A sanctioned name",
                  "value": {
                    "value": "Vladimir Putin"
                  }
                },
                "business": {
                  "summary": "A legal entity name (GLEIF + enrichment)",
                  "value": {
                    "value": "Apple Inc."
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The verdict for the artifact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerdictResult"
                },
                "examples": {
                  "green": {
                    "summary": "A business with a verified LEI",
                    "value": {
                      "verdict": "green",
                      "confidence": 0.85,
                      "artifact": {
                        "type": "payee",
                        "value": "Apple Inc."
                      },
                      "reason": "No safety problems found. Active LEI match found for Apple Inc.: HWUPKR0MPOU8FGXBT394 (US-CA).",
                      "recommended_action": "allow",
                      "signals": [
                        {
                          "name": "payee_format",
                          "severity": "info",
                          "detail": "Not a recognized payment-handle format; treated as a name or other identifier.",
                          "source": "payee_format"
                        },
                        {
                          "name": "sanctions_clean",
                          "severity": "info",
                          "detail": "No sanctions-list match for this payee name.",
                          "source": "opensanctions"
                        },
                        {
                          "name": "lei_verified",
                          "severity": "low",
                          "detail": "Active LEI match found for Apple Inc.: HWUPKR0MPOU8FGXBT394 (US-CA).",
                          "source": "gleif"
                        }
                      ],
                      "request_id": "98f13708210194c475687be6106a3b84",
                      "cached": false,
                      "first_seen": "2026-06-10T08:00:00Z",
                      "checked_at": "2026-06-19T11:32:00Z"
                    }
                  },
                  "red": {
                    "summary": "A sanctions-list match",
                    "value": {
                      "verdict": "red",
                      "confidence": 0.97,
                      "artifact": {
                        "type": "payee",
                        "value": "Vladimir Putin"
                      },
                      "reason": "Payee matches a sanctioned entity: Vladimir Vladimirovich PUTIN.",
                      "recommended_action": "block",
                      "signals": [
                        {
                          "name": "sanctions_match",
                          "severity": "critical",
                          "detail": "Payee matches a sanctioned entity: Vladimir Vladimirovich PUTIN.",
                          "source": "opensanctions"
                        }
                      ],
                      "request_id": "aab3238922bcc25a6f606eb525ffdc56",
                      "cached": false,
                      "first_seen": "2026-06-19T11:33:00Z",
                      "checked_at": "2026-06-19T11:33:00Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": "unauthorized",
                  "message": "Invalid API key.",
                  "request_id": "b1946ac92492d2347c6235b4d2611184"
                }
              }
            }
          },
          "422": {
            "description": "Request body failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": "invalid_request",
                  "message": "Request body failed validation.",
                  "request_id": "b1946ac92492d2347c6235b4d2611184"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded \u2014 slow down.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": "rate_limited",
                  "message": "Too many requests. Slow down.",
                  "request_id": "b1946ac92492d2347c6235b4d2611184"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    },
    "/v1/verify/message": {
      "post": {
        "tags": [
          "Verify"
        ],
        "summary": "Verify a message",
        "description": "Check inbound text (SMS, chat, email) before acting on it. Extracts any links and verifies each (folding in the worst), then reasons about scam tactics \u2014 urgency, impersonation, OTP/PIN solicitation.",
        "operationId": "verify_message_v1_verify_message_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ValueIn"
              },
              "examples": {
                "otp_scam": {
                  "summary": "An OTP-solicitation scam",
                  "value": {
                    "value": "URGENT: Your account is blocked. Share the OTP we just sent to reactivate."
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The verdict for the artifact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerdictResult"
                },
                "examples": {
                  "yellow": {
                    "summary": "An OTP-phishing message (text only)",
                    "value": {
                      "verdict": "yellow",
                      "confidence": 0.5,
                      "artifact": {
                        "type": "message",
                        "value": "URGENT: Your account is blocked. Share the OTP we just sent to reactivate."
                      },
                      "reason": "Pressures you to share a one-time passcode \u2014 a hallmark of account-takeover scams.",
                      "recommended_action": "warn",
                      "signals": [
                        {
                          "name": "llm_assessment",
                          "severity": "medium",
                          "detail": "Pressures you to share a one-time passcode \u2014 a hallmark of account-takeover scams. No legitimate service asks for an OTP.",
                          "source": "llm"
                        }
                      ],
                      "request_id": "9bf31c7ff062936a96d3c8bd1f8f2ff3",
                      "cached": false,
                      "first_seen": "2026-06-19T11:34:00Z",
                      "checked_at": "2026-06-19T11:34:00Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": "unauthorized",
                  "message": "Invalid API key.",
                  "request_id": "b1946ac92492d2347c6235b4d2611184"
                }
              }
            }
          },
          "422": {
            "description": "Request body failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": "invalid_request",
                  "message": "Request body failed validation.",
                  "request_id": "b1946ac92492d2347c6235b4d2611184"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded \u2014 slow down.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": "rate_limited",
                  "message": "Too many requests. Slow down.",
                  "request_id": "b1946ac92492d2347c6235b4d2611184"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    },
    "/v1/verify": {
      "post": {
        "tags": [
          "Verify"
        ],
        "summary": "Verify any artifact",
        "description": "The generic entry point: the body carries the `type` (`url`, `payee`, or `message`) alongside `value`. Behaves exactly like the typed routes.",
        "operationId": "verify_generic_v1_verify_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyIn"
              },
              "examples": {
                "url": {
                  "summary": "Verify a URL",
                  "value": {
                    "type": "url",
                    "value": "https://www.google.com"
                  }
                },
                "payee": {
                  "summary": "Verify a payee",
                  "value": {
                    "type": "payee",
                    "value": "ramesh@okaxis"
                  }
                },
                "message": {
                  "summary": "Verify a message",
                  "value": {
                    "type": "message",
                    "value": "Click here to claim your refund: bit.ly/x"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The verdict for the artifact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerdictResult"
                },
                "examples": {
                  "url_green": {
                    "summary": "A clean URL",
                    "value": {
                      "verdict": "green",
                      "confidence": 0.8,
                      "artifact": {
                        "type": "url",
                        "value": "https://www.google.com"
                      },
                      "reason": "No safety problems found for this link.",
                      "recommended_action": "allow",
                      "signals": [
                        {
                          "name": "safe_browsing_clean",
                          "severity": "info",
                          "detail": "No Google Safe Browsing match.",
                          "source": "safe_browsing"
                        },
                        {
                          "name": "domain_age_days",
                          "severity": "info",
                          "detail": "Domain has been registered for 9863 day(s).",
                          "source": "rdap"
                        }
                      ],
                      "request_id": "b1946ac92492d2347c6235b4d2611184",
                      "cached": false,
                      "first_seen": "2026-06-01T09:12:44Z",
                      "checked_at": "2026-06-19T11:30:05Z"
                    }
                  },
                  "payee_red": {
                    "summary": "A sanctioned payee",
                    "value": {
                      "verdict": "red",
                      "confidence": 0.97,
                      "artifact": {
                        "type": "payee",
                        "value": "Vladimir Putin"
                      },
                      "reason": "Payee matches a sanctioned entity: Vladimir Vladimirovich PUTIN.",
                      "recommended_action": "block",
                      "signals": [
                        {
                          "name": "sanctions_match",
                          "severity": "critical",
                          "detail": "Payee matches a sanctioned entity: Vladimir Vladimirovich PUTIN.",
                          "source": "opensanctions"
                        }
                      ],
                      "request_id": "aab3238922bcc25a6f606eb525ffdc56",
                      "cached": false,
                      "first_seen": "2026-06-19T11:33:00Z",
                      "checked_at": "2026-06-19T11:33:00Z"
                    }
                  },
                  "message_yellow": {
                    "summary": "A suspicious message",
                    "value": {
                      "verdict": "yellow",
                      "confidence": 0.5,
                      "artifact": {
                        "type": "message",
                        "value": "URGENT: Your account is blocked. Share the OTP we just sent to reactivate."
                      },
                      "reason": "Pressures you to share a one-time passcode \u2014 a hallmark of account-takeover scams.",
                      "recommended_action": "warn",
                      "signals": [
                        {
                          "name": "llm_assessment",
                          "severity": "medium",
                          "detail": "Pressures you to share a one-time passcode \u2014 a hallmark of account-takeover scams. No legitimate service asks for an OTP.",
                          "source": "llm"
                        }
                      ],
                      "request_id": "9bf31c7ff062936a96d3c8bd1f8f2ff3",
                      "cached": false,
                      "first_seen": "2026-06-19T11:34:00Z",
                      "checked_at": "2026-06-19T11:34:00Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": "unauthorized",
                  "message": "Invalid API key.",
                  "request_id": "b1946ac92492d2347c6235b4d2611184"
                }
              }
            }
          },
          "422": {
            "description": "Request body failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": "invalid_request",
                  "message": "Request body failed validation.",
                  "request_id": "b1946ac92492d2347c6235b4d2611184"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded \u2014 slow down.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": "rate_limited",
                  "message": "Too many requests. Slow down.",
                  "request_id": "b1946ac92492d2347c6235b4d2611184"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    },
    "/v1/reports": {
      "post": {
        "tags": [
          "Reports"
        ],
        "summary": "Report a bad actor or wrong verdict",
        "description": "Submit first-party feedback when Faro got it wrong or when you know an artifact is malicious. The report is stored on the normalized reputation node, **invalidates any cached green/yellow verdict** for that node, and feeds future checks via tier-2 reputation signals.",
        "operationId": "create_report_v1_reports_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReportIn"
              },
              "examples": {
                "scam_url": {
                  "summary": "Report a phishing link Faro missed",
                  "value": {
                    "type": "url",
                    "value": "http://sbi-kyc-update.xyz/verify",
                    "kind": "phishing"
                  }
                },
                "false_positive": {
                  "summary": "Faro flagged a legitimate payee incorrectly",
                  "value": {
                    "type": "payee",
                    "value": "legitimate-vendor@okaxis",
                    "kind": "false_positive"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Report accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReportAccepted"
                },
                "example": {
                  "accepted": true,
                  "artifact_id": "domain:evil-shop.xyz",
                  "message": "Report recorded. The next check on this artifact will re-evaluate."
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerAuth": []
          }
        ]
      }
    },
    "/v1/keys/request": {
      "post": {
        "tags": [
          "Keys"
        ],
        "summary": "Request an API key by email",
        "description": "Sends a one-time magic link to the email address; clicking it issues the key (see `/v1/keys/confirm`). Always returns 202 \u2014 the response never reveals whether the address exists.",
        "operationId": "request_key_v1_keys_request_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KeyRequestIn"
              }
            }
          },
          "required": true
        },
        "responses": {
          "202": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": {
                    "type": "string"
                  },
                  "type": "object",
                  "title": "Response Request Key V1 Keys Request Post"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/keys/confirm": {
      "post": {
        "tags": [
          "Keys"
        ],
        "summary": "Confirm the magic link and receive the key",
        "description": "Exchanges a valid magic-link token for a new API key. The key is returned **once** \u2014 store it now; only its hash is kept server-side.",
        "operationId": "confirm_key_v1_keys_confirm_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KeyConfirmIn"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyIssued"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "tags": [
          "Dashboard"
        ],
        "summary": "Profile for the authenticated API key",
        "description": "Returns the caller's key prefix, email, mode (`live` or `test`), and issue time. Requires a self-serve issued key \u2014 admin keys receive 403.",
        "operationId": "get_me_v1_me_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MeOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Admin or dev-mode credentials cannot use dashboard routes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/v1/me/usage": {
      "get": {
        "tags": [
          "Dashboard"
        ],
        "summary": "Per-key call counts",
        "description": "Daily call counts (by artifact type) for the caller's key over a trailing window. Best-effort \u2014 derived from the lightweight usage log.",
        "operationId": "get_my_usage_v1_me_usage_get",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 30,
              "title": "Days"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Admin or dev-mode credentials cannot use dashboard routes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/me/verdicts": {
      "get": {
        "tags": [
          "Dashboard"
        ],
        "summary": "Recent verdicts for this key",
        "description": "The most recent verdicts the caller's key produced (normalized node, type, verdict, time). Best-effort \u2014 derived from the usage log.",
        "operationId": "get_my_verdicts_v1_me_verdicts_get",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 10,
              "title": "Limit"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerdictsOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Admin or dev-mode credentials cannot use dashboard routes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/me/keys/test": {
      "post": {
        "tags": [
          "Dashboard"
        ],
        "summary": "Issue a test (sandbox) key",
        "description": "Issues a `faro_test_*` key for the caller's email. Test keys return deterministic fixtures (no external checks, no cost) and never write to the reputation graph \u2014 see the test-mode fixture table in the docs.",
        "operationId": "create_test_key_v1_me_keys_test_post",
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyIssued"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Admin or dev-mode credentials cannot use dashboard routes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/v1/me/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhook endpoints",
        "description": "List the caller's registered endpoints (secrets are never returned).",
        "operationId": "list_webhooks_v1_me_webhooks_get",
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/WebhookOut"
                  },
                  "type": "array",
                  "title": "Response List Webhooks V1 Me Webhooks Get"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Admin or dev-mode credentials cannot use dashboard routes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Register a webhook endpoint",
        "description": "Register an HTTPS endpoint to receive signed `artifact.flagged` events. The response includes the signing `secret` **once** \u2014 store it now; verify incoming deliveries with the `X-Faro-Signature` header.",
        "operationId": "create_webhook_v1_me_webhooks_post",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreateIn"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookCreated"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Admin or dev-mode credentials cannot use dashboard routes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/me/webhooks/{endpoint_id}": {
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook endpoint",
        "operationId": "delete_webhook_v1_me_webhooks__endpoint_id__delete",
        "parameters": [
          {
            "name": "endpoint_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Endpoint Id"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Successful Response"
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Admin or dev-mode credentials cannot use dashboard routes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/me/webhooks/{endpoint_id}/ping": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Send a test event",
        "description": "Deliver a one-off `webhook.ping` to verify the endpoint is wired up.",
        "operationId": "ping_webhook_v1_me_webhooks__endpoint_id__ping_post",
        "parameters": [
          {
            "name": "endpoint_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Endpoint Id"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookPingOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Admin or dev-mode credentials cannot use dashboard routes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/me/webhooks/{endpoint_id}/deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Recent delivery attempts",
        "description": "The most recent delivery attempts for an endpoint (debugging).",
        "operationId": "list_webhook_deliveries_v1_me_webhooks__endpoint_id__deliveries_get",
        "parameters": [
          {
            "name": "endpoint_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Endpoint Id"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 20,
              "title": "Limit"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WebhookDeliveryOut"
                  },
                  "title": "Response List Webhook Deliveries V1 Me Webhooks  Endpoint Id  Deliveries Get"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "Admin or dev-mode credentials cannot use dashboard routes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Action": {
        "type": "string",
        "enum": [
          "allow",
          "warn",
          "block",
          "review"
        ],
        "title": "Action"
      },
      "Artifact": {
        "properties": {
          "type": {
            "$ref": "#/components/schemas/ArtifactType",
            "description": "The kind of artifact that was verified."
          },
          "value": {
            "type": "string",
            "title": "Value",
            "description": "The normalized artifact string that was checked."
          }
        },
        "type": "object",
        "required": [
          "type",
          "value"
        ],
        "title": "Artifact"
      },
      "ArtifactType": {
        "type": "string",
        "enum": [
          "url",
          "payee",
          "message"
        ],
        "title": "ArtifactType"
      },
      "ErrorEnvelope": {
        "properties": {
          "error": {
            "type": "string",
            "title": "Error",
            "description": "Stable machine-readable error code.",
            "examples": [
              "unauthorized"
            ]
          },
          "message": {
            "type": "string",
            "title": "Message",
            "description": "Human-readable explanation.",
            "examples": [
              "Invalid API key."
            ]
          },
          "request_id": {
            "type": "string",
            "title": "Request Id",
            "description": "The id of the request that failed (also in the `X-Request-ID` header).",
            "examples": [
              "b1946ac92492d2347c6235b4d2611184"
            ]
          }
        },
        "type": "object",
        "required": [
          "error",
          "message",
          "request_id"
        ],
        "title": "ErrorEnvelope",
        "description": "The consistent error shape returned by every non-2xx response.\n\nNever a leaked stack trace \u2014 always `{ error, message, request_id }`. Quote\n`request_id` when reporting a problem; it ties the response to server logs."
      },
      "HTTPValidationError": {
        "properties": {
          "detail": {
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            },
            "type": "array",
            "title": "Detail"
          }
        },
        "type": "object",
        "title": "HTTPValidationError"
      },
      "KeyConfirmIn": {
        "properties": {
          "token": {
            "type": "string",
            "title": "Token",
            "description": "The signed token from the emailed link."
          }
        },
        "type": "object",
        "required": [
          "token"
        ],
        "title": "KeyConfirmIn",
        "description": "Body for `POST /v1/keys/confirm` \u2014 the token from the magic link."
      },
      "KeyIssued": {
        "properties": {
          "api_key": {
            "type": "string",
            "title": "Api Key",
            "description": "The full API key. Store it now; shown only once.",
            "examples": [
              "faro_live_3pZ8..."
            ]
          },
          "key_prefix": {
            "type": "string",
            "title": "Key Prefix",
            "description": "A non-secret prefix for identifying the key later.",
            "examples": [
              "faro_live_3pZ8Qa"
            ]
          },
          "email": {
            "type": "string",
            "title": "Email",
            "description": "The verified email the key was issued to."
          }
        },
        "type": "object",
        "required": [
          "api_key",
          "key_prefix",
          "email"
        ],
        "title": "KeyIssued",
        "description": "The newly issued key \u2014 returned once; only its hash is stored server-side."
      },
      "KeyRequestIn": {
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "title": "Email",
            "description": "Where to send the API-key link.",
            "examples": [
              "dev@acme.com"
            ]
          },
          "location": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Location",
            "description": "Optional signup metadata (city, country).",
            "examples": [
              "Mumbai, India"
            ]
          }
        },
        "type": "object",
        "required": [
          "email"
        ],
        "title": "KeyRequestIn",
        "description": "Body for `POST /v1/keys/request` \u2014 the email to send a magic link to."
      },
      "MeOut": {
        "properties": {
          "key_prefix": {
            "type": "string",
            "title": "Key Prefix",
            "description": "Non-secret key identifier, e.g. `faro_live_AbC123`."
          },
          "email": {
            "type": "string",
            "title": "Email",
            "description": "Email the key was issued to."
          },
          "mode": {
            "type": "string",
            "title": "Mode",
            "description": "Key mode: `live` or `test`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At",
            "description": "When this key was issued."
          },
          "label": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Label",
            "description": "Optional human label for the key."
          }
        },
        "type": "object",
        "required": [
          "key_prefix",
          "email",
          "mode",
          "created_at"
        ],
        "title": "MeOut",
        "description": "Profile for the authenticated API key."
      },
      "ReportAccepted": {
        "properties": {
          "accepted": {
            "type": "boolean",
            "title": "Accepted",
            "description": "Always `true` on success.",
            "default": true
          },
          "artifact_id": {
            "type": "string",
            "title": "Artifact Id",
            "description": "Normalized reputation node the report attached to.",
            "examples": [
              "domain:evil-shop.xyz"
            ]
          },
          "message": {
            "type": "string",
            "title": "Message",
            "description": "Human-readable confirmation.",
            "examples": [
              "Report recorded. The next check on this artifact will re-evaluate."
            ]
          }
        },
        "type": "object",
        "required": [
          "artifact_id",
          "message"
        ],
        "title": "ReportAccepted",
        "description": "Acknowledgement that a report was recorded."
      },
      "ReportIn": {
        "properties": {
          "type": {
            "$ref": "#/components/schemas/ArtifactType",
            "description": "Which kind of artifact `value` is."
          },
          "value": {
            "type": "string",
            "maxLength": 8192,
            "minLength": 1,
            "title": "Value",
            "description": "The artifact to report (URL, payee handle, or message text)."
          },
          "kind": {
            "$ref": "#/components/schemas/ReportKind",
            "description": "Report category. Use `false_positive` when Faro flagged something incorrectly; other kinds mark the artifact as bad."
          }
        },
        "type": "object",
        "required": [
          "type",
          "value",
          "kind"
        ],
        "title": "ReportIn",
        "description": "Body for `POST /v1/reports` \u2014 flag a bad actor or a wrong verdict."
      },
      "ReportKind": {
        "type": "string",
        "enum": [
          "scam",
          "phishing",
          "malware",
          "false_positive",
          "other"
        ],
        "title": "ReportKind",
        "description": "Why the caller is reporting this artifact."
      },
      "Severity": {
        "type": "string",
        "enum": [
          "info",
          "low",
          "medium",
          "high",
          "critical"
        ],
        "title": "Severity"
      },
      "Signal": {
        "properties": {
          "name": {
            "type": "string",
            "title": "Name",
            "description": "Machine-readable signal id, e.g. `domain_age_days`."
          },
          "severity": {
            "$ref": "#/components/schemas/Severity",
            "description": "How strongly this signal affects the verdict."
          },
          "detail": {
            "type": "string",
            "title": "Detail",
            "description": "Human-readable explanation of what was found."
          },
          "source": {
            "type": "string",
            "title": "Source",
            "description": "Integration that produced the signal, e.g. `safe_browsing`."
          }
        },
        "type": "object",
        "required": [
          "name",
          "severity",
          "detail",
          "source"
        ],
        "title": "Signal"
      },
      "UsageDayOut": {
        "properties": {
          "day": {
            "type": "string",
            "format": "date",
            "title": "Day",
            "description": "UTC date of the bucket."
          },
          "artifact_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Artifact Type",
            "description": "`url`, `payee`, `message`, or null for non-verify calls."
          },
          "count": {
            "type": "integer",
            "title": "Count",
            "description": "Number of calls in this bucket."
          }
        },
        "type": "object",
        "required": [
          "day",
          "count"
        ],
        "title": "UsageDayOut",
        "description": "Calls on one day, for one artifact type."
      },
      "UsageOut": {
        "properties": {
          "key_prefix": {
            "type": "string",
            "title": "Key Prefix",
            "description": "The key these counts belong to."
          },
          "days": {
            "type": "integer",
            "title": "Days",
            "description": "Size of the trailing window, in days."
          },
          "total": {
            "type": "integer",
            "title": "Total",
            "description": "Total calls in the window."
          },
          "daily": {
            "items": {
              "$ref": "#/components/schemas/UsageDayOut"
            },
            "type": "array",
            "title": "Daily",
            "description": "Per-day, per-type counts (most recent first)."
          }
        },
        "type": "object",
        "required": [
          "key_prefix",
          "days",
          "total",
          "daily"
        ],
        "title": "UsageOut",
        "description": "Per-key usage over a trailing window."
      },
      "ValidationError": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          },
          "input": {
            "title": "Input"
          },
          "ctx": {
            "type": "object",
            "title": "Context"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "ValueIn": {
        "properties": {
          "value": {
            "type": "string",
            "title": "Value",
            "description": "The artifact to verify (a URL, payee, or message string)."
          }
        },
        "type": "object",
        "required": [
          "value"
        ],
        "title": "ValueIn",
        "description": "Body for the typed routes: `{ \"value\": \"...\" }`."
      },
      "Verdict": {
        "type": "string",
        "enum": [
          "green",
          "yellow",
          "red"
        ],
        "title": "Verdict"
      },
      "VerdictOut": {
        "properties": {
          "artifact": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Artifact",
            "description": "Normalized reputation node, e.g. `domain:evil.example`."
          },
          "artifact_type": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Artifact Type",
            "description": "`url`, `payee`, or `message`."
          },
          "verdict": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Verdict",
            "description": "`green`, `yellow`, or `red`."
          },
          "checked_at": {
            "type": "string",
            "format": "date-time",
            "title": "Checked At",
            "description": "When the call was made."
          }
        },
        "type": "object",
        "required": [
          "checked_at"
        ],
        "title": "VerdictOut",
        "description": "One recent verdict the key produced."
      },
      "VerdictResult": {
        "properties": {
          "verdict": {
            "$ref": "#/components/schemas/Verdict",
            "description": "Traffic-light verdict: green, yellow, or red."
          },
          "confidence": {
            "type": "number",
            "maximum": 1.0,
            "minimum": 0.0,
            "title": "Confidence",
            "description": "Calibrated confidence in the verdict (honest about data completeness)."
          },
          "artifact": {
            "$ref": "#/components/schemas/Artifact"
          },
          "reason": {
            "type": "string",
            "title": "Reason",
            "description": "One plain-language line explaining the verdict \u2014 always present."
          },
          "recommended_action": {
            "$ref": "#/components/schemas/Action",
            "description": "What to do next: allow, warn, block, or review."
          },
          "signals": {
            "items": {
              "$ref": "#/components/schemas/Signal"
            },
            "type": "array",
            "title": "Signals",
            "description": "Evidence behind the verdict \u2014 deterministic checks, enrichment, reputation."
          },
          "explanation": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Explanation",
            "description": "Optional longer context (e.g. session repeat-check notes)."
          },
          "request_id": {
            "type": "string",
            "title": "Request Id",
            "description": "Unique id for this request (also in the `X-Request-ID` response header)."
          },
          "cached": {
            "type": "boolean",
            "title": "Cached",
            "description": "True when served from the verdict cache (`X-Faro-Cache: hit`).",
            "default": false
          },
          "first_seen": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "First Seen",
            "description": "When Faro first saw this normalized node in the reputation graph."
          },
          "checked_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Checked At",
            "description": "When this verdict was computed."
          }
        },
        "type": "object",
        "required": [
          "verdict",
          "confidence",
          "artifact",
          "reason",
          "recommended_action",
          "request_id"
        ],
        "title": "VerdictResult",
        "description": "The single verdict object returned by every verify endpoint and SDK method."
      },
      "VerdictsOut": {
        "properties": {
          "key_prefix": {
            "type": "string",
            "title": "Key Prefix",
            "description": "The key these verdicts belong to."
          },
          "verdicts": {
            "items": {
              "$ref": "#/components/schemas/VerdictOut"
            },
            "type": "array",
            "title": "Verdicts",
            "description": "Most recent first."
          }
        },
        "type": "object",
        "required": [
          "key_prefix",
          "verdicts"
        ],
        "title": "VerdictsOut",
        "description": "Recent verdict history for the caller's key."
      },
      "VerifyIn": {
        "properties": {
          "type": {
            "$ref": "#/components/schemas/ArtifactType",
            "description": "Which kind of artifact `value` is."
          },
          "value": {
            "type": "string",
            "title": "Value",
            "description": "The artifact to verify."
          }
        },
        "type": "object",
        "required": [
          "type",
          "value"
        ],
        "title": "VerifyIn",
        "description": "Body for the generic `POST /v1/verify`: carries the artifact type."
      },
      "WebhookCreateIn": {
        "properties": {
          "url": {
            "type": "string",
            "title": "Url",
            "description": "HTTPS endpoint Faro POSTs signed events to.",
            "examples": [
              "https://hooks.acme.com/faro"
            ]
          },
          "events": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "title": "Events",
            "description": "Events to subscribe to. Defaults to `[\"artifact.flagged\"]`."
          },
          "label": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Label",
            "description": "Optional human label for this endpoint."
          }
        },
        "type": "object",
        "required": [
          "url"
        ],
        "title": "WebhookCreateIn",
        "description": "Register a receiver for the caller's key."
      },
      "WebhookCreated": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id",
            "description": "Endpoint id, e.g. `whk_\u2026`."
          },
          "url": {
            "type": "string",
            "title": "Url",
            "description": "Where events are delivered."
          },
          "events": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Events",
            "description": "Subscribed event types."
          },
          "active": {
            "type": "boolean",
            "title": "Active",
            "description": "Whether deliveries are currently sent."
          },
          "label": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Label",
            "description": "Optional human label."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At",
            "description": "When the endpoint was registered."
          },
          "secret": {
            "type": "string",
            "title": "Secret",
            "description": "HMAC signing secret (`whsec_\u2026`). Store it now \u2014 it is never shown again."
          }
        },
        "type": "object",
        "required": [
          "id",
          "url",
          "events",
          "active",
          "created_at",
          "secret"
        ],
        "title": "WebhookCreated",
        "description": "Creation response \u2014 includes the signing secret, shown exactly once."
      },
      "WebhookDeliveryOut": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id",
            "description": "Delivery id, e.g. `whd_\u2026`."
          },
          "event_type": {
            "type": "string",
            "title": "Event Type",
            "description": "The event delivered."
          },
          "status": {
            "type": "string",
            "title": "Status",
            "description": "`pending`, `delivered`, or `failed`."
          },
          "attempts": {
            "type": "integer",
            "title": "Attempts",
            "description": "Number of POST attempts so far."
          },
          "last_status_code": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Status Code",
            "description": "HTTP status of the most recent attempt."
          },
          "last_error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Error",
            "description": "Most recent failure reason."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At",
            "description": "When the event was enqueued."
          },
          "delivered_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ],
            "title": "Delivered At",
            "description": "When it was successfully delivered."
          }
        },
        "type": "object",
        "required": [
          "id",
          "event_type",
          "status",
          "attempts",
          "created_at"
        ],
        "title": "WebhookDeliveryOut",
        "description": "One delivery attempt record (for debugging)."
      },
      "WebhookOut": {
        "properties": {
          "id": {
            "type": "string",
            "title": "Id",
            "description": "Endpoint id, e.g. `whk_\u2026`."
          },
          "url": {
            "type": "string",
            "title": "Url",
            "description": "Where events are delivered."
          },
          "events": {
            "items": {
              "type": "string"
            },
            "type": "array",
            "title": "Events",
            "description": "Subscribed event types."
          },
          "active": {
            "type": "boolean",
            "title": "Active",
            "description": "Whether deliveries are currently sent."
          },
          "label": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Label",
            "description": "Optional human label."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "title": "Created At",
            "description": "When the endpoint was registered."
          }
        },
        "type": "object",
        "required": [
          "id",
          "url",
          "events",
          "active",
          "created_at"
        ],
        "title": "WebhookOut",
        "description": "A registered endpoint (no secret)."
      },
      "WebhookPingOut": {
        "properties": {
          "delivered": {
            "type": "boolean",
            "title": "Delivered",
            "description": "Whether the receiver returned 2xx."
          },
          "status_code": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Status Code",
            "description": "Receiver's HTTP status."
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error",
            "description": "Failure reason, if any."
          }
        },
        "type": "object",
        "required": [
          "delivered"
        ],
        "title": "WebhookPingOut",
        "description": "Result of a one-shot test delivery."
      }
    },
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Faro API key. Send as `Authorization: Bearer <key>`. Get a key at https://farofinance.app or via `/v1/keys/request`."
      }
    }
  },
  "tags": [
    {
      "name": "Verify",
      "description": "Get a verdict for a URL, payee, or message \u2014 the core of the API."
    },
    {
      "name": "Keys",
      "description": "Self-serve API-key issuance via an email magic link."
    },
    {
      "name": "Reports",
      "description": "Report a bad actor or a wrong verdict \u2014 feeds the reputation graph."
    },
    {
      "name": "System",
      "description": "Health and liveness."
    },
    {
      "name": "Dashboard",
      "description": "Self-serve visibility for your API key \u2014 usage and recent verdicts."
    },
    {
      "name": "Webhooks",
      "description": "Register endpoints to receive signed `artifact.flagged` events."
    }
  ],
  "servers": [
    {
      "url": "https://api.farofinance.app",
      "description": "Production"
    },
    {
      "url": "http://localhost:8000",
      "description": "Local development"
    }
  ]
}
