{
 "openapi": "3.0.3",
 "info": {
  "title": "Truing API",
  "version": "1.0.0",
  "description": "Truing \u2014 UK entity resolution and registry signals. Domain -> company, company -> domain, with evidence."
 },
 "paths": {
  "/v1/enrich": {
   "post": {
    "summary": "Resolve and enrich a company domain",
    "security": [
     {
      "ApiKeyAuth": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "domain"
        ],
        "properties": {
         "domain": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Cached enrichment payload"
     },
     "202": {
      "description": "Enrichment job queued"
     },
     "400": {
      "description": "Invalid request"
     },
     "401": {
      "description": "Unauthorized"
     },
     "402": {
      "description": "No credits"
     }
    }
   }
  },
  "/v1/enrich/status/{job_id}": {
   "get": {
    "summary": "Get enrichment job status",
    "parameters": [
     {
      "name": "job_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Job status"
     },
     "404": {
      "description": "Job not found"
     }
    }
   }
  },
  "/v1/resolve/domain": {
   "post": {
    "summary": "Agent-friendly domain resolution",
    "security": [
     {
      "ApiKeyAuth": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "domain"
        ],
        "properties": {
         "domain": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Resolution payload"
     },
     "202": {
      "description": "Resolution job queued"
     }
    }
   }
  },
  "/v1/resolve/company": {
   "post": {
    "summary": "Resolve a Companies House company profile",
    "security": [
     {
      "ApiKeyAuth": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "company_number": {
          "type": "string"
         },
         "company_name": {
          "type": "string"
         },
         "refresh": {
          "type": "boolean"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Company profile, current signals, and reverse-domain status"
     },
     "404": {
      "description": "Company not found"
     }
    }
   }
  },
  "/v1/watchlist": {
   "get": {
    "summary": "List watched companies",
    "security": [
     {
      "BearerAuth": []
     }
    ],
    "responses": {
     "200": {
      "description": "Watchlist"
     }
    }
   },
   "post": {
    "summary": "Watch a company",
    "security": [
     {
      "BearerAuth": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "company_number"
        ],
        "properties": {
         "company_number": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "Company watched"
     },
     "200": {
      "description": "Already watched"
     },
     "402": {
      "description": "Watchlist limit reached"
     },
     "404": {
      "description": "Company not found"
     }
    }
   }
  },
  "/v1/watchlist/{company_number}": {
   "delete": {
    "summary": "Unwatch a company",
    "security": [
     {
      "BearerAuth": []
     }
    ],
    "parameters": [
     {
      "name": "company_number",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Company unwatched"
     }
    }
   }
  },
  "/v1/signals": {
   "get": {
    "summary": "List watched-company signals",
    "security": [
     {
      "BearerAuth": []
     }
    ],
    "parameters": [
     {
      "name": "signal_type",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "page",
      "in": "query",
      "schema": {
       "type": "integer",
       "default": 1
      }
     },
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "default": 20
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Scoped signal feed"
     }
    }
   }
  },
  "/v1/companies/{company_number}/signals": {
   "get": {
    "summary": "List recent signals for a company",
    "security": [
     {
      "BearerAuth": []
     }
    ],
    "parameters": [
     {
      "name": "company_number",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "default": 20
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Company signal history"
     },
     "404": {
      "description": "Company not found"
     }
    }
   }
  },
  "/v1/clay/enrich-company": {
   "post": {
    "summary": "Enrich a company domain for Clay",
    "security": [{ "ApiKeyAuth": [] }],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": ["domain"],
        "properties": { "domain": { "type": "string", "example": "example.co.uk" } }
       }
      }
     }
    },
    "responses": {
     "200": { "description": "Completed flat Clay enrichment", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClayTerminalResponse" } } } },
     "202": { "description": "Fresh resolution queued", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ClayPendingResponse" } } } },
     "400": { "description": "Invalid domain" },
     "401": { "description": "Unauthorized" },
     "402": { "description": "No credits" },
     "429": { "description": "Rate limited" }
    }
   }
  },
  "/v1/clay/enrich-company/{job_id}": {
   "get": {
    "summary": "Retrieve an owned Clay enrichment job",
    "security": [{ "ApiKeyAuth": [] }],
    "parameters": [{ "name": "job_id", "in": "path", "required": true, "schema": { "type": "string" } }],
    "responses": {
     "200": {
      "description": "Current job state or completed flat result",
      "content": { "application/json": { "schema": { "oneOf": [{ "$ref": "#/components/schemas/ClayPendingResponse" }, { "$ref": "#/components/schemas/ClayTerminalResponse" }] } } }
     },
     "401": { "description": "Unauthorized" },
     "404": { "description": "Job not found for this account" }
    }
   }
  },
  "/v1/clay/webhooks": {
   "get": {
    "summary": "List webhook destinations",
    "security": [
     {
      "BearerAuth": []
     }
    ],
    "responses": {
     "200": {
      "description": "Webhook list"
     }
    }
   },
   "post": {
    "summary": "Register webhook destination",
    "security": [
     {
      "BearerAuth": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "webhook_url",
         "signal_type"
        ],
        "properties": {
         "webhook_url": {
          "type": "string",
          "format": "uri"
         },
         "signal_type": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "Webhook registered"
     }
    }
   }
  },
  "/v1/clay/webhooks/{id}": {
   "delete": {
    "summary": "Delete webhook destination",
    "security": [
     {
      "BearerAuth": []
     }
    ],
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Webhook deleted"
     }
    }
   }
  },
  "/v1/auth/signup": {
   "post": {
    "summary": "Create an unverified account and send a verification email",
    "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["email", "password"], "properties": { "email": { "type": "string", "format": "email" }, "password": { "type": "string", "minLength": 8 } } } } } },
    "responses": { "201": { "description": "Verification email sent; no session is created" }, "409": { "description": "Account already exists" } }
   }
  },
  "/v1/auth/login": {
   "post": {
    "summary": "Sign in after email verification",
    "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["email", "password"], "properties": { "email": { "type": "string", "format": "email" }, "password": { "type": "string" } } } } } },
    "responses": { "200": { "description": "JWT session and account API key" }, "401": { "description": "Invalid credentials or email not verified" } }
   }
  },
  "/v1/auth/verify-email": {
   "get": {
    "summary": "Consume a one-use email-verification token and redirect to sign in",
    "parameters": [{ "name": "token", "in": "query", "required": true, "schema": { "type": "string" } }],
    "responses": { "302": { "description": "Redirect to sign in with verification result" } }
   }
  },
  "/v1/auth/forgot-password": {
   "post": {
    "summary": "Request a password reset; response never reveals account existence",
    "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["email"], "properties": { "email": { "type": "string", "format": "email" } } } } } },
    "responses": { "200": { "description": "Generic reset response" } }
   }
  },
  "/v1/auth/reset-password": {
   "post": {
    "summary": "Consume a one-use reset token and invalidate older sessions",
    "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["token", "new_password"], "properties": { "token": { "type": "string" }, "new_password": { "type": "string", "minLength": 8 } } } } } },
    "responses": { "200": { "description": "Password updated" }, "400": { "description": "Invalid or expired token" } }
   }
  },
  "/v1/auth/me": {
   "get": {
    "summary": "Account status: credits, subscription, watchlist usage, API key metadata",
    "security": [
     {
      "BearerAuth": []
     },
     {
      "ApiKeyAuth": []
     }
    ],
    "responses": {
     "200": {
      "description": "Account profile with credit balance and subscription"
     },
     "401": {
      "description": "Unauthorized"
     }
    }
   }
  },
  "/v1/companies/search": {
   "get": {
    "summary": "Search the UK register by name, postcode, status, accounts type, incorporation dates",
    "security": [
     {
      "BearerAuth": []
     },
     {
      "ApiKeyAuth": []
     }
    ],
    "parameters": [
     {
      "name": "name",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "postcode",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "company_status",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "accounts_type",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "incorporated_from",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "incorporated_to",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "page",
      "in": "query",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Paginated company candidates"
     },
     "401": {
      "description": "Unauthorized"
     }
    }
   }
  },
  "/v1/auth/change-password": {
   "post": {
    "summary": "Change password (requires current password)",
    "security": [
     {
      "BearerAuth": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "current_password": {
          "type": "string"
         },
         "new_password": {
          "type": "string",
          "minLength": 8
         }
        },
        "required": [
         "current_password",
         "new_password"
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Password updated"
     },
     "401": {
      "description": "Current password incorrect"
     }
    }
   }
  },
  "/healthz": {
   "get": {
    "summary": "Liveness probe (includes DB reachability)",
    "responses": {
     "200": {
      "description": "Healthy"
     },
     "503": {
      "description": "Degraded"
     }
    }
   }
  },
  "/mcp": {
   "post": {
    "summary": "Hosted MCP endpoint (JSON-RPC) for AI agents; API key or OAuth bearer token",
    "security": [
     {
      "ApiKeyAuth": []
     },
     {
      "OAuthMcp": ["truing:mcp"]
     }
    ],
    "responses": {
     "200": {
      "description": "JSON-RPC response"
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "BearerAuth": {
    "type": "http",
    "scheme": "bearer"
   },
   "ApiKeyAuth": {
    "type": "apiKey",
    "in": "header",
    "name": "X-API-Key"
   },
   "OAuthMcp": {
    "type": "oauth2",
    "flows": {
     "authorizationCode": {
      "authorizationUrl": "https://truingdata.com/oauth/authorize",
      "tokenUrl": "https://truingdata.com/oauth/token",
      "scopes": { "truing:mcp": "Use Truing MCP tools on the account's credit ledger" }
     }
    }
   }
  },
  "schemas": {
   "ClayPendingResponse": {
    "type": "object",
    "required": ["status", "input_domain", "job_id", "charged"],
    "properties": {
     "status": { "type": "string", "enum": ["pending", "processing", "failed"] },
     "input_domain": { "type": "string" },
     "job_id": { "type": "string" },
     "charged": { "type": "boolean" },
     "charge_reason": { "type": "string" },
     "status_url": { "type": "string" },
     "error_message": { "type": "string", "nullable": true }
    }
   },
   "ClayTerminalResponse": {
    "type": "object",
    "required": ["status", "input_domain", "charged", "charge_reason", "result"],
    "properties": {
     "status": { "type": "string", "enum": ["completed"] },
     "input_domain": { "type": "string" },
     "charged": { "type": "boolean" },
     "charge_reason": { "type": "string" },
     "result": { "$ref": "#/components/schemas/ClayCompanyResult" }
    }
   },
   "ClayCompanyResult": {
    "type": "object",
    "required": ["match_status", "recent_resignations", "review_candidates"],
    "properties": {
     "match_status": { "type": "string", "enum": ["matched", "needs_review", "no_match", "non_uk_entity", "scrape_failed"] },
     "company_name": { "type": "string", "nullable": true },
     "company_number": { "type": "string", "nullable": true },
     "company_status": { "type": "string", "nullable": true },
     "registered_postcode": { "type": "string", "nullable": true },
     "incorporated_on": { "type": "string", "format": "date", "nullable": true },
     "match_confidence": { "type": "number", "nullable": true },
     "accounts_type": { "type": "string", "nullable": true },
     "accounts_next_due": { "type": "string", "format": "date", "nullable": true },
     "accounts_overdue": { "type": "boolean", "nullable": true },
     "accounts_days_until_due": { "type": "integer", "nullable": true },
     "confirmation_next_due": { "type": "string", "format": "date", "nullable": true },
     "confirmation_overdue": { "type": "boolean", "nullable": true },
     "confirmation_days_until_due": { "type": "integer", "nullable": true },
     "has_insolvency_history": { "type": "boolean", "nullable": true },
     "current_insolvency_status": { "type": "string", "nullable": true },
     "has_active_charges": { "type": "boolean", "nullable": true },
     "last_charge_date": { "type": "string", "format": "date", "nullable": true },
     "days_since_last_charge": { "type": "integer", "nullable": true },
     "recent_director_resignation": { "type": "boolean", "nullable": true },
     "last_director_change_date": { "type": "string", "format": "date", "nullable": true },
     "days_since_last_director_change": { "type": "integer", "nullable": true },
     "recent_resignations": { "type": "array", "items": { "type": "object" } },
     "review_candidates": { "type": "array", "items": { "type": "object" } }
    }
   }
  }
 },
 "servers": [
  {
   "url": "https://api.truingdata.com"
  },
  {
   "url": "https://truingdata.com"
  }
 ]
}
