{
  "openapi": "3.0.3",
  "info": {
    "title": "Qualia API",
    "description": "Public HTTP API for Qualia. Signup enrichment for PLG SaaS.",
    "version": "0.0.1",
    "contact": {
      "name": "Qualia Support",
      "url": "https://github.com/sebi75/qualia"
    }
  },
  "servers": [
    {
      "url": "https://qualia.so/v1",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Actors",
      "description": "One actor per person. Create one with an email, fetch the briefing once enrichment completes."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/actors": {
      "post": {
        "operationId": "createActor",
        "summary": "Create an actor",
        "description": "Creates an actor for the given email and enqueues enrichment. Idempotent on email within an organization. Returns 201 with status \"created\" for a new actor, or 200 with status \"already_exists\" if an actor with this email already exists in your organization.",
        "tags": [
          "Actors"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "maxLength": 320,
                    "format": "email",
                    "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
                    "description": "Email address of the actor (required, RFC 3696 max length)."
                  },
                  "name": {
                    "description": "Display name for the actor, if known.",
                    "type": "string",
                    "maxLength": 255
                  },
                  "source": {
                    "description": "Free-form label describing where the actor came from (e.g. 'signup_form', 'admin_import'). First-touch only; never overwritten on subsequent calls.",
                    "type": "string",
                    "maxLength": 50
                  },
                  "metadata": {
                    "description": "Arbitrary additional fields to attach to the actor. The API does not enforce a schema on this object. Send whatever context you have (company, title, plan, UTM params, onboarding progress, etc.). Stored verbatim and available to the enrichment agent as additional context.",
                    "type": "object",
                    "additionalProperties": {}
                  }
                },
                "required": [
                  "email"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "An actor with this email already existed in the organization. No new enrichment was triggered.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "actorId": {
                      "type": "string",
                      "description": "Stable identifier for the actor."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "created",
                        "already_exists"
                      ],
                      "description": "`created` for a newly-upserted actor (enrichment enqueued), `already_exists` if an actor with this email already existed in the organization."
                    }
                  },
                  "required": [
                    "actorId",
                    "status"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "201": {
            "description": "A new actor was created and enrichment has been enqueued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "actorId": {
                      "type": "string",
                      "description": "Stable identifier for the actor."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "created",
                        "already_exists"
                      ],
                      "description": "`created` for a newly-upserted actor (enrichment enqueued), `already_exists` if an actor with this email already existed in the organization."
                    }
                  },
                  "required": [
                    "actorId",
                    "status"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400"
          },
          "401": {
            "$ref": "#/components/responses/401"
          },
          "404": {
            "$ref": "#/components/responses/404"
          },
          "422": {
            "$ref": "#/components/responses/422"
          },
          "500": {
            "$ref": "#/components/responses/500"
          }
        }
      }
    },
    "/actors/{actorId}": {
      "get": {
        "operationId": "getActor",
        "summary": "Fetch an actor",
        "description": "Returns an actor record. The enrichment and intelligence fields are populated once the actor's status is \"enriched\". Poll until status reaches \"enriched\" (success) or \"failed\" (terminal failure).",
        "tags": [
          "Actors"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "actorId",
            "schema": {
              "type": "string",
              "description": "Stable identifier returned from POST /v1/actors."
            },
            "required": true,
            "description": "Stable identifier returned from POST /v1/actors."
          }
        ],
        "responses": {
          "200": {
            "description": "The actor record. Enrichment fields are populated once status is \"enriched\".",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Stable identifier for the actor."
                    },
                    "email": {
                      "type": "string",
                      "description": "Lowercased email address of the actor."
                    },
                    "name": {
                      "nullable": true,
                      "description": "Display name, if provided on ingest.",
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "enriching",
                        "enriched",
                        "failed"
                      ],
                      "description": "Lifecycle status of enrichment. `pending` right after ingest, transitions through `enriching` to `enriched` (success) or `failed`."
                    },
                    "source": {
                      "nullable": true,
                      "description": "First-touch source label captured at ingest time (never overwritten).",
                      "type": "string"
                    },
                    "enrichment": {
                      "nullable": true,
                      "description": "Structured facts about the person + company found by the enrichment agent. Null until enrichment completes.",
                      "type": "object",
                      "properties": {
                        "fullName": {
                          "type": "string"
                        },
                        "firstName": {
                          "type": "string"
                        },
                        "lastName": {
                          "type": "string"
                        },
                        "title": {
                          "type": "string"
                        },
                        "seniority": {
                          "type": "string"
                        },
                        "department": {
                          "type": "string"
                        },
                        "linkedinUrl": {
                          "type": "string"
                        },
                        "twitterHandle": {
                          "type": "string"
                        },
                        "githubUsername": {
                          "type": "string"
                        },
                        "bio": {
                          "type": "string"
                        },
                        "location": {
                          "type": "string"
                        },
                        "photoUrl": {
                          "type": "string"
                        },
                        "companyName": {
                          "type": "string"
                        },
                        "companyDomain": {
                          "type": "string"
                        },
                        "companyDescription": {
                          "type": "string"
                        },
                        "industry": {
                          "type": "string"
                        },
                        "employeeCount": {
                          "type": "number"
                        },
                        "employeeRange": {
                          "type": "string"
                        },
                        "fundingStage": {
                          "type": "string"
                        },
                        "totalFunding": {
                          "type": "number"
                        },
                        "techStack": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "companyLinkedinUrl": {
                          "type": "string"
                        },
                        "companyLocation": {
                          "type": "string"
                        },
                        "citations": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "object",
                            "properties": {
                              "url": {
                                "nullable": true,
                                "type": "string"
                              },
                              "tool": {
                                "type": "string"
                              },
                              "title": {
                                "type": "string"
                              },
                              "retrievedAt": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "tool"
                            ],
                            "additionalProperties": false
                          }
                        },
                        "sources": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "confidence": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1
                        },
                        "enrichedAt": {
                          "type": "string"
                        }
                      },
                      "additionalProperties": false
                    },
                    "intelligence": {
                      "nullable": true,
                      "description": "The agent's analysis and outreach recommendations. Null until enrichment completes.",
                      "type": "object",
                      "properties": {
                        "summary": {
                          "type": "string"
                        },
                        "highlights": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "concerns": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "careerTrajectory": {
                          "type": "string"
                        },
                        "communicationStyle": {
                          "type": "string"
                        },
                        "interests": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "motivations": {
                          "type": "string"
                        },
                        "strategicPriorities": {
                          "type": "string"
                        },
                        "painPoints": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "techStackReality": {
                          "type": "string"
                        },
                        "timingSignals": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "urgency": {
                          "type": "string"
                        },
                        "outreachStrategy": {
                          "type": "string"
                        },
                        "suggestedChannel": {
                          "type": "string"
                        },
                        "suggestedHook": {
                          "type": "string"
                        },
                        "suggestedTone": {
                          "type": "string"
                        },
                        "citations": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "url": {
                                  "nullable": true,
                                  "type": "string"
                                },
                                "tool": {
                                  "type": "string"
                                },
                                "title": {
                                  "type": "string"
                                },
                                "retrievedAt": {
                                  "type": "string"
                                }
                              },
                              "required": [
                                "tool"
                              ],
                              "additionalProperties": false
                            }
                          }
                        }
                      },
                      "additionalProperties": false
                    },
                    "score": {
                      "nullable": true,
                      "description": "ICP fit score in 0-100. Null until scoring completes (no ICP profile, ICP not ready, or scoring failed).",
                      "type": "number",
                      "minimum": 0,
                      "maximum": 100
                    },
                    "scoring": {
                      "nullable": true,
                      "description": "Structured scoring output: explanation, matched/unmatched rules, breakdown by dimension. Null until scoring completes or if scoring failed.",
                      "type": "object",
                      "properties": {
                        "explanation": {
                          "type": "string"
                        },
                        "matchedRules": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "unmatchedRules": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "breakdown": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "number"
                          }
                        }
                      },
                      "additionalProperties": false
                    },
                    "createdAt": {
                      "type": "string",
                      "format": "date-time",
                      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
                      "description": "ISO 8601 timestamp of when the actor was first ingested."
                    }
                  },
                  "required": [
                    "id",
                    "email",
                    "name",
                    "status",
                    "source",
                    "enrichment",
                    "intelligence",
                    "score",
                    "scoring",
                    "createdAt"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/400"
          },
          "401": {
            "$ref": "#/components/responses/401"
          },
          "404": {
            "$ref": "#/components/responses/404"
          },
          "422": {
            "$ref": "#/components/responses/422"
          },
          "500": {
            "$ref": "#/components/responses/500"
          }
        }
      }
    }
  },
  "components": {
    "responses": {
      "400": {
        "description": "The request was malformed (invalid JSON, missing required headers, etc.)",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "enum": [
                        "bad_request"
                      ],
                      "description": "Machine-readable error code"
                    },
                    "message": {
                      "type": "string",
                      "description": "Human-readable error message"
                    },
                    "requestId": {
                      "type": "string",
                      "description": "Unique request identifier. Include it when filing a support ticket."
                    }
                  },
                  "required": [
                    "code",
                    "message",
                    "requestId"
                  ],
                  "additionalProperties": false
                }
              },
              "required": [
                "error"
              ],
              "additionalProperties": false
            }
          }
        }
      },
      "401": {
        "description": "Authentication failed (missing, malformed, revoked, or expired API key)",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "enum": [
                        "unauthorized"
                      ],
                      "description": "Machine-readable error code"
                    },
                    "message": {
                      "type": "string",
                      "description": "Human-readable error message"
                    },
                    "requestId": {
                      "type": "string",
                      "description": "Unique request identifier. Include it when filing a support ticket."
                    }
                  },
                  "required": [
                    "code",
                    "message",
                    "requestId"
                  ],
                  "additionalProperties": false
                }
              },
              "required": [
                "error"
              ],
              "additionalProperties": false
            }
          }
        }
      },
      "404": {
        "description": "The requested resource was not found",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "enum": [
                        "not_found"
                      ],
                      "description": "Machine-readable error code"
                    },
                    "message": {
                      "type": "string",
                      "description": "Human-readable error message"
                    },
                    "requestId": {
                      "type": "string",
                      "description": "Unique request identifier. Include it when filing a support ticket."
                    }
                  },
                  "required": [
                    "code",
                    "message",
                    "requestId"
                  ],
                  "additionalProperties": false
                }
              },
              "required": [
                "error"
              ],
              "additionalProperties": false
            }
          }
        }
      },
      "422": {
        "description": "The request was well-formed but failed schema validation",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "enum": [
                        "unprocessable_entity"
                      ],
                      "description": "Machine-readable error code"
                    },
                    "message": {
                      "type": "string",
                      "description": "Human-readable error message"
                    },
                    "requestId": {
                      "type": "string",
                      "description": "Unique request identifier. Include it when filing a support ticket."
                    }
                  },
                  "required": [
                    "code",
                    "message",
                    "requestId"
                  ],
                  "additionalProperties": false
                }
              },
              "required": [
                "error"
              ],
              "additionalProperties": false
            }
          }
        }
      },
      "500": {
        "description": "An unexpected server error occurred",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "properties": {
                "error": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "enum": [
                        "internal_server_error"
                      ],
                      "description": "Machine-readable error code"
                    },
                    "message": {
                      "type": "string",
                      "description": "Human-readable error message"
                    },
                    "requestId": {
                      "type": "string",
                      "description": "Unique request identifier. Include it when filing a support ticket."
                    }
                  },
                  "required": [
                    "code",
                    "message",
                    "requestId"
                  ],
                  "additionalProperties": false
                }
              },
              "required": [
                "error"
              ],
              "additionalProperties": false
            }
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key issued via the dashboard. Format: `qk_live_<random>`."
      }
    }
  }
}
