openapi: 3.0.3 info: title: happyDeliver API description: Email Deliverability Testing Platform API version: 0.1.0 contact: name: happyDomain team url: https://github.com/happyDomain/happydeliver email: contact+api@happydomain.org license: name: GNU Affero General Public License v3.0 or later url: https://spdx.org/licenses/AGPL-3.0-or-later.html servers: - url: http://localhost:8080/api description: Local development server - url: https://api.example.com/api description: Production server tags: - name: tests description: Test management operations - name: reports description: Report retrieval operations - name: health description: Service health and status paths: /test: post: tags: - tests summary: Create a new deliverability test description: Generates a unique test email address for sending test emails. No database record is created until an email is received. operationId: createTest responses: '201': description: Test email address generated successfully content: application/json: schema: $ref: '#/components/schemas/TestResponse' '500': description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' /test/{id}: get: tags: - tests summary: Get test status description: Check if a report exists for the given test ID (base32-encoded). Returns pending if no report exists, analyzed if a report is available. operationId: getTest parameters: - name: id in: path required: true schema: type: string pattern: '^[a-z0-9-]+$' description: Base32-encoded test ID (with hyphens) responses: '200': description: Test status retrieved successfully content: application/json: schema: $ref: '#/components/schemas/Test' '500': description: Internal server error content: application/json: schema: $ref: '#/components/schemas/Error' /report/{id}: get: tags: - reports summary: Get detailed report description: Retrieve comprehensive deliverability analysis report operationId: getReport parameters: - name: id in: path required: true schema: type: string pattern: '^[a-z0-9-]+$' description: Base32-encoded test ID (with hyphens) responses: '200': description: Report retrieved successfully content: application/json: schema: $ref: '#/components/schemas/Report' '404': description: Report not found content: application/json: schema: $ref: '#/components/schemas/Error' /report/{id}/raw: get: tags: - reports summary: Get raw annotated email description: Retrieve the original email with headers added by filters operationId: getRawEmail parameters: - name: id in: path required: true schema: type: string pattern: '^[a-z0-9-]+$' description: Base32-encoded test ID (with hyphens) responses: '200': description: Raw email retrieved successfully content: text/plain: schema: type: string '404': description: Email not found content: application/json: schema: $ref: '#/components/schemas/Error' /report/{id}/reanalyze: post: tags: - reports summary: Reanalyze email and regenerate report description: Re-run the analysis on the stored raw email to regenerate the report with the latest analyzer version. This is useful after analyzer improvements or bug fixes. operationId: reanalyzeReport parameters: - name: id in: path required: true schema: type: string pattern: '^[a-z0-9-]+$' description: Base32-encoded test ID (with hyphens) responses: '200': description: Report regenerated successfully content: application/json: schema: $ref: '#/components/schemas/Report' '404': description: Email not found content: application/json: schema: $ref: '#/components/schemas/Error' '500': description: Internal server error during reanalysis content: application/json: schema: $ref: '#/components/schemas/Error' /status: get: tags: - health summary: Service health check description: Get service health status and component versions operationId: getStatus responses: '200': description: Service status content: application/json: schema: $ref: '#/components/schemas/Status' components: schemas: Test: type: object required: - id - email - status properties: id: type: string pattern: '^[a-z0-9-]+$' description: Unique test identifier (base32-encoded with hyphens) example: "krfwg4z-amrqw4z-zmorsw2-djmfzgk-3a" email: type: string format: email description: Unique test email address example: "test-krfwg4z-amrqw4z-zmorsw2-djmfzgk-3a@example.com" status: type: string enum: [pending, analyzed] description: Current test status (pending = no report yet, analyzed = report available) example: "analyzed" TestResponse: type: object required: - id - email - status properties: id: type: string pattern: '^[a-z0-9-]+$' description: Unique test identifier (base32-encoded with hyphens) example: "krfwg4z-amrqw4z-zmorsw2-djmfzgk-3a" email: type: string format: email example: "test-krfwg4z-amrqw4z-zmorsw2-djmfzgk-3a@example.com" status: type: string enum: [pending] example: "pending" message: type: string example: "Send your test email to the address above" Report: type: object required: - id - test_id - score - grade - checks - created_at properties: id: type: string pattern: '^[a-z0-9-]+$' description: Report identifier (base32-encoded with hyphens) test_id: type: string pattern: '^[a-z0-9-]+$' description: Associated test ID (base32-encoded with hyphens) score: type: number format: float minimum: 0 maximum: 100 description: Overall deliverability score as percentage (0-100) example: 85 grade: type: string enum: [A+, A, B, C, D, E, F] description: Letter grade representation of the score (A+ is best, F is worst) example: "A" summary: $ref: '#/components/schemas/ScoreSummary' checks: type: array items: $ref: '#/components/schemas/Check' authentication: $ref: '#/components/schemas/AuthenticationResults' spamassassin: $ref: '#/components/schemas/SpamAssassinResult' dns_records: type: array items: $ref: '#/components/schemas/DNSRecord' blacklists: type: array items: $ref: '#/components/schemas/BlacklistCheck' raw_headers: type: string description: Raw email headers created_at: type: string format: date-time ScoreSummary: type: object required: - authentication_score - spam_score - blacklist_score - content_score - header_score properties: authentication_score: type: number format: float minimum: 0 maximum: 100 description: SPF/DKIM/DMARC score (in percentage) example: 28 spam_score: type: number format: float minimum: 0 maximum: 100 description: SpamAssassin score (in percentage) example: 15 blacklist_score: type: number format: float minimum: 0 maximum: 100 description: Blacklist check score (in percentage) example: 20 content_score: type: number format: float minimum: 0 maximum: 100 description: Content quality score (in percentage) example: 18 header_score: type: number format: float minimum: 0 maximum: 100 description: Header quality score (in percentage) example: 9 Check: type: object required: - category - name - status - score - grade - message properties: category: type: string enum: [authentication, dns, content, blacklist, headers, spam] description: Check category example: "authentication" name: type: string description: Check name example: "DKIM Signature" status: type: string enum: [pass, fail, warn, info, error] description: Check result status example: "pass" score: type: number format: float description: Points contributed to total score example: 10 grade: type: string enum: [A+, A, B, C, D, E, F] description: Letter grade representation of the score (A+ is best, F is worst) example: "A" message: type: string description: Human-readable result message example: "DKIM signature is valid" details: type: string description: Additional details (may be JSON) severity: type: string enum: [critical, high, medium, low, info] description: Issue severity example: "info" advice: type: string description: Remediation advice example: "Your DKIM configuration is correct" AuthenticationResults: type: object properties: spf: $ref: '#/components/schemas/AuthResult' dkim: type: array items: $ref: '#/components/schemas/AuthResult' dmarc: $ref: '#/components/schemas/AuthResult' bimi: $ref: '#/components/schemas/AuthResult' arc: $ref: '#/components/schemas/ARCResult' AuthResult: type: object required: - result properties: result: type: string enum: [pass, fail, none, neutral, softfail, temperror, permerror] description: Authentication result example: "pass" domain: type: string description: Domain being authenticated example: "example.com" selector: type: string description: DKIM selector (for DKIM only) example: "default" details: type: string description: Additional details about the result ARCResult: type: object required: - result properties: result: type: string enum: [pass, fail, none] description: Overall ARC chain validation result example: "pass" chain_valid: type: boolean description: Whether the ARC chain signatures are valid example: true chain_length: type: integer description: Number of ARC sets in the chain example: 2 details: type: string description: Additional details about ARC validation example: "ARC chain valid with 2 intermediaries" SpamAssassinResult: type: object required: - score - required_score - is_spam properties: score: type: number format: float description: SpamAssassin spam score example: 2.3 required_score: type: number format: float description: Threshold for spam classification example: 5.0 is_spam: type: boolean description: Whether message is classified as spam example: false tests: type: array items: type: string description: List of triggered SpamAssassin tests example: ["BAYES_00", "DKIM_SIGNED"] report: type: string description: Full SpamAssassin report DNSRecord: type: object required: - domain - record_type - status properties: domain: type: string description: Domain name example: "example.com" record_type: type: string enum: [MX, SPF, DKIM, DMARC, BIMI] description: DNS record type example: "SPF" status: type: string enum: [found, missing, invalid] description: Record status example: "found" value: type: string description: Record value example: "v=spf1 include:_spf.example.com ~all" BlacklistCheck: type: object required: - ip - rbl - listed properties: ip: type: string description: IP address checked example: "192.0.2.1" rbl: type: string description: RBL/DNSBL name example: "zen.spamhaus.org" listed: type: boolean description: Whether IP is listed example: false response: type: string description: RBL response code or message example: "127.0.0.2" Status: type: object required: - status - version properties: status: type: string enum: [healthy, degraded, unhealthy] description: Overall service status example: "healthy" version: type: string description: Service version example: "0.1.0-dev" components: type: object properties: database: type: string enum: [up, down] example: "up" mta: type: string enum: [up, down] example: "up" uptime: type: integer description: Service uptime in seconds example: 3600 Error: type: object required: - error - message properties: error: type: string description: Error code example: "not_found" message: type: string description: Human-readable error message example: "Test not found" details: type: string description: Additional error details