{
  "openapi": "3.0.3",
  "info": {
    "title": "noscha.io API",
    "description": "Disposable email, subdomain & NIP-05 identity service powered by Lightning Network payments.",
    "version": "1.0.0",
    "contact": {
      "email": "admin@noscha.io"
    }
  },
  "servers": [
    {
      "url": "https://noscha.io"
    }
  ],
  "paths": {
    "/api/check/{username}": {
      "get": {
        "operationId": "checkUsername",
        "summary": "Check username availability",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 20,
              "pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Availability result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckUsernameResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/order": {
      "post": {
        "operationId": "createOrder",
        "summary": "Create a new rental order with Lightning invoice",
        "description": "Creates an order and sends webhook_challenge to webhook_url. After payment is confirmed, payment_completed is POSTed with order_id, username, management_token, my_page_url (https://noscha.io/my/{token}), expires_at, plan, amount_sats, is_extend, services.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Order created with Lightning invoice",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request"
          },
          "409": {
            "description": "Username already taken"
          }
        }
      }
    },
    "/api/order/{order_id}/status": {
      "get": {
        "operationId": "getOrderStatus",
        "summary": "Poll order payment/provisioning status",
        "parameters": [
          {
            "name": "order_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Order status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderStatusResponse"
                }
              }
            }
          },
          "404": {
            "description": "Order not found"
          }
        }
      }
    },
    "/api/extend": {
      "post": {
        "operationId": "extendRental",
        "summary": "Extend an existing rental",
        "description": "Extends the expiry of an existing rental by creating a new extension order. Time is added on top of current expiry (not from now).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExtendRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Extension invoice created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExtendResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body (malformed JSON)"
          },
          "404": {
            "description": "Rental not found (invalid management_token)"
          }
        }
      }
    },
    "/api/add-service": {
      "post": {
        "operationId": "addService",
        "summary": "Add new services to an existing rental",
        "description": "Adds new services (email, subdomain, NIP-05) to an existing rental. Cannot add services that are already active. The new services get the specified plan duration independently. Each service will have its own expires_at timestamp. Creates a Lightning invoice for the cost of the new services. After payment, services are activated with their individual expiration times.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AddServiceRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Service addition invoice created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddServiceResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body (malformed JSON, no services to add, or missing plan parameter)"
          },
          "404": {
            "description": "Rental not found (invalid management_token)"
          },
          "409": {
            "description": "Service already active (Email, Subdomain, or NIP-05 already provisioned for this rental)"
          }
        }
      }
    },
    "/api/pricing": {
      "get": {
        "operationId": "getPricing",
        "summary": "Get current pricing for all plans and services",
        "responses": {
          "200": {
            "description": "Pricing matrix",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/info": {
      "get": {
        "operationId": "getServiceInfo",
        "summary": "Get service metadata",
        "responses": {
          "200": {
            "description": "Service info"
          }
        }
      }
    },
    "/health": {
      "get": {
        "operationId": "healthCheck",
        "summary": "Health check",
        "responses": {
          "200": {
            "description": "Health status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/settings/{management_token}": {
      "put": {
        "operationId": "updateSettings",
        "summary": "Update rental settings (webhook URL)",
        "parameters": [
          {
            "name": "management_token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "webhook_url": {
                    "type": "string",
                    "nullable": true,
                    "description": "Webhook URL for email notifications. Set to null to disable."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Settings updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "webhook_url": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Rental not found"
          }
        }
      }
    },
    "/api/my/{token}/nip05": {
      "put": {
        "operationId": "updateNip05",
        "summary": "Update NIP-05 pubkey for Nostr identity",
        "description": "Update the Nostr NIP-05 public key for an existing rental. Pubkey is normalized to hex format.",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Management token"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateNip05Request"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "NIP-05 pubkey updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateNip05Response"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body, invalid pubkey format (must be 64-char hex or npub1...), or NIP-05 service not configured/enabled"
          },
          "404": {
            "description": "Rental not found (invalid management token)"
          }
        }
      }
    },
    "/api/my/{token}/subdomain": {
      "put": {
        "operationId": "updateSubdomain",
        "summary": "Update subdomain CNAME target",
        "description": "Update the CNAME target for the subdomain service. Updates DNS record immediately with no payment required.",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Management token"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSubdomainRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Subdomain target updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UpdateSubdomainResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body or Subdomain service not configured/enabled"
          },
          "404": {
            "description": "Rental not found (invalid management token)"
          },
          "500": {
            "description": "Failed to update DNS record (Cloudflare API error)"
          }
        }
      }
    },
    "/api/mail/{username}": {
      "get": {
        "operationId": "listMail",
        "summary": "List all emails in inbox",
        "description": "Returns a list of emails with metadata for the given username.",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Management token for authentication"
          }
        ],
        "responses": {
          "200": {
            "description": "List of emails",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MailSummary"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token"
          },
          "403": {
            "description": "Account not active, expired, or email service not enabled"
          },
          "404": {
            "description": "User not found"
          }
        }
      }
    },
    "/api/mail/{username}/{mail_id}": {
      "get": {
        "operationId": "getMailById",
        "summary": "Get a specific email by ID",
        "description": "Retrieve full email data including body. Marks email as read on first access.",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "mail_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Email ID (e.g. m_xxxx)"
          },
          {
            "name": "token",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Management token (optional if using view_url from webhook)"
          }
        ],
        "responses": {
          "200": {
            "description": "Full email data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailDetail"
                }
              }
            }
          },
          "401": {
            "description": "Invalid token"
          },
          "404": {
            "description": "Email not found"
          }
        }
      }
    },
    "/api/mail/{username}/send": {
      "post": {
        "operationId": "sendMail",
        "summary": "Send an email from your noscha.io address",
        "description": "Send an email via Resend API. Rate limited to 5 emails per 24 hours per account. From address will be {username}@noscha.io.",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SendMailRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Email sent successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SendMailResponse"
                }
              }
            }
          },
          "400": {
            "description": "Missing required fields (to, subject, text)"
          },
          "401": {
            "description": "Missing or invalid management token"
          },
          "403": {
            "description": "Account not active, expired, or email service not enabled"
          },
          "429": {
            "description": "Daily send limit reached (5/day)"
          },
          "503": {
            "description": "Email service temporarily unavailable"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "CheckUsernameResponse": {
        "type": "object",
        "properties": {
          "available": {
            "type": "boolean"
          },
          "username": {
            "type": "string"
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "available",
          "username"
        ]
      },
      "OrderRequest": {
        "type": "object",
        "required": [
          "username",
          "plan"
        ],
        "properties": {
          "username": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20
          },
          "plan": {
            "type": "string",
            "enum": [
              "1d",
              "7d",
              "30d",
              "90d",
              "365d"
            ]
          },
          "services": {
            "$ref": "#/components/schemas/ServicesRequest"
          }
        }
      },
      "ServicesRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "object"
          },
          "subdomain": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "A",
                  "AAAA",
                  "CNAME"
                ]
              },
              "target": {
                "type": "string"
              },
              "proxied": {
                "type": "boolean",
                "default": false
              }
            },
            "required": [
              "type",
              "target"
            ]
          },
          "nip05": {
            "type": "object",
            "properties": {
              "pubkey": {
                "type": "string",
                "description": "Hex-encoded Nostr public key"
              }
            },
            "required": [
              "pubkey"
            ]
          }
        }
      },
      "OrderResponse": {
        "type": "object",
        "properties": {
          "order_id": {
            "type": "string"
          },
          "amount_sats": {
            "type": "integer"
          },
          "bolt11": {
            "type": "string",
            "description": "Lightning Network invoice"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "management_token": {
            "type": "string"
          }
        },
        "required": [
          "order_id",
          "amount_sats",
          "bolt11",
          "expires_at"
        ]
      },
      "OrderStatusResponse": {
        "type": "object",
        "properties": {
          "order_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "paid",
              "provisioned",
              "expired"
            ]
          },
          "management_token": {
            "type": "string"
          }
        },
        "required": [
          "order_id",
          "status"
        ]
      },
      "ExtendRequest": {
        "type": "object",
        "required": [
          "management_token",
          "plan"
        ],
        "properties": {
          "management_token": {
            "type": "string"
          },
          "plan": {
            "type": "string",
            "enum": [
              "1d",
              "7d",
              "30d",
              "90d",
              "365d"
            ]
          },
          "services": {
            "$ref": "#/components/schemas/ServicesRequest"
          },
          "service_keys": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "email",
                "subdomain",
                "nip05"
              ]
            },
            "description": "Optional: specify which services to extend. If omitted, extends all active services."
          }
        }
      },
      "ExtendResponse": {
        "type": "object",
        "properties": {
          "order_id": {
            "type": "string"
          },
          "amount_sats": {
            "type": "integer"
          },
          "bolt11": {
            "type": "string"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "order_id",
          "amount_sats",
          "bolt11",
          "expires_at"
        ]
      },
      "AddServiceRequest": {
        "type": "object",
        "required": [
          "management_token",
          "plan"
        ],
        "properties": {
          "management_token": {
            "type": "string"
          },
          "plan": {
            "type": "string",
            "enum": [
              "1d",
              "7d",
              "30d",
              "90d",
              "365d"
            ],
            "description": "Duration for the new services being added"
          },
          "services": {
            "$ref": "#/components/schemas/ServicesRequest"
          }
        }
      },
      "AddServiceResponse": {
        "type": "object",
        "properties": {
          "order_id": {
            "type": "string"
          },
          "amount_sats": {
            "type": "integer"
          },
          "bolt11": {
            "type": "string"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "order_id",
          "amount_sats",
          "bolt11",
          "expires_at"
        ]
      },
      "UpdateNip05Request": {
        "type": "object",
        "required": [
          "pubkey"
        ],
        "properties": {
          "pubkey": {
            "type": "string",
            "description": "Nostr public key (hex or npub format)"
          }
        }
      },
      "UpdateNip05Response": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "pubkey_hex": {
            "type": "string",
            "description": "Normalized hex-encoded public key"
          }
        },
        "required": [
          "success",
          "pubkey_hex"
        ]
      },
      "UpdateSubdomainRequest": {
        "type": "object",
        "required": [
          "target"
        ],
        "properties": {
          "target": {
            "type": "string",
            "description": "New CNAME target for the subdomain"
          }
        }
      },
      "UpdateSubdomainResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "target": {
            "type": "string",
            "description": "New CNAME target"
          }
        },
        "required": [
          "success",
          "target"
        ]
      },
      "MailSummary": {
        "type": "object",
        "properties": {
          "mail_id": {
            "type": "string"
          },
          "from": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "date": {
            "type": "string"
          },
          "read_at": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string"
          }
        },
        "required": [
          "mail_id",
          "from",
          "subject",
          "date",
          "created_at"
        ]
      },
      "MailDetail": {
        "type": "object",
        "properties": {
          "mail_id": {
            "type": "string"
          },
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "body_text": {
            "type": "string",
            "nullable": true
          },
          "body_html": {
            "type": "string",
            "nullable": true
          },
          "date": {
            "type": "string"
          },
          "created_at": {
            "type": "string"
          },
          "read_at": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "mail_id",
          "from",
          "to",
          "subject",
          "date",
          "created_at"
        ]
      },
      "SendMailRequest": {
        "type": "object",
        "required": [
          "to",
          "subject",
          "text",
          "management_token"
        ],
        "properties": {
          "to": {
            "type": "string",
            "description": "Recipient email address"
          },
          "subject": {
            "type": "string",
            "description": "Email subject"
          },
          "text": {
            "type": "string",
            "description": "Plain text email body"
          },
          "management_token": {
            "type": "string",
            "description": "Management token for authentication"
          }
        }
      },
      "SendMailResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "message_id": {
            "type": "string"
          }
        },
        "required": [
          "success",
          "message_id"
        ]
      }
    }
  }
}