{
  "openapi": "3.1.0",
  "info": {
    "title": "TablePro License API",
    "version": "1.0.0",
    "description": "API for managing TablePro license activations, validations, and deactivations across machines."
  },
  "servers": [
    {
      "url": "https://api.tablepro.app"
    }
  ],
  "tags": [
    {
      "name": "License",
      "description": "License management operations"
    }
  ],
  "paths": {
    "/v1/license/activate": {
      "post": {
        "operationId": "activateLicense",
        "summary": "Activate a license",
        "description": "Activates a license on a specific machine. If the machine is already activated, the activation record is updated with the latest app and OS version.",
        "tags": [
          "License"
        ],
        "x-mint": {
          "title": "Activate License",
          "description": "Activate a license key on a specific machine"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActivateRequest"
              },
              "example": {
                "license_key": "ABCDE-12345-FGHIJ-67890-KLMNO",
                "machine_id": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
                "machine_name": "John's MacBook Pro",
                "app_version": "0.2.0",
                "os_version": "macOS 15.3"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "License activated successfully. Returns signed license data.",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "The maximum number of requests allowed in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "The number of requests remaining in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "The Unix timestamp when the rate limit window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignedLicenseResponse"
                },
                "example": {
                  "data": {
                    "email": "john@example.com",
                    "expires_at": "2027-02-11T00:00:00+00:00",
                    "issued_at": "2026-02-12T10:30:00+00:00",
                    "license_key": "ABCDE-12345-FGHIJ-67890-KLMNO",
                    "status": "active"
                  },
                  "signature": "base64-encoded-signature"
                }
              }
            }
          },
          "403": {
            "description": "License suspended or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "suspended": {
                    "summary": "License suspended",
                    "value": {
                      "message": "This license has been suspended."
                    }
                  },
                  "expired": {
                    "summary": "License expired",
                    "value": {
                      "message": "This license has expired."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "License key not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "License key not found."
                }
              }
            }
          },
          "409": {
            "description": "Activation limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "Activation limit reached."
                }
              }
            }
          },
          "422": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                },
                "example": {
                  "message": "The given data was invalid.",
                  "errors": {
                    "license_key": [
                      "The license key field is required."
                    ],
                    "machine_id": [
                      "The machine id must be 64 characters."
                    ]
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitResponse"
                },
                "example": {
                  "message": "Too many requests.",
                  "retry_after": 60
                }
              }
            }
          }
        }
      }
    },
    "/v1/license/validate": {
      "post": {
        "operationId": "validateLicense",
        "summary": "Validate a license",
        "description": "Validates that a license is active and the machine is registered. Used for periodic license checks.",
        "tags": [
          "License"
        ],
        "x-mint": {
          "title": "Validate License",
          "description": "Validate that a license is active and the machine is registered"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ValidateRequest"
              },
              "example": {
                "license_key": "ABCDE-12345-FGHIJ-67890-KLMNO",
                "machine_id": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "License is valid. Returns signed license data.",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "The maximum number of requests allowed in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "The number of requests remaining in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "The Unix timestamp when the rate limit window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SignedLicenseResponse"
                },
                "example": {
                  "data": {
                    "email": "john@example.com",
                    "expires_at": "2027-02-11T00:00:00+00:00",
                    "issued_at": "2026-02-12T10:30:00+00:00",
                    "license_key": "ABCDE-12345-FGHIJ-67890-KLMNO",
                    "status": "active"
                  },
                  "signature": "base64-encoded-signature"
                }
              }
            }
          },
          "403": {
            "description": "Machine not activated, license suspended, or license expired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "not_activated": {
                    "summary": "Machine not activated",
                    "value": {
                      "message": "This machine is not activated."
                    }
                  },
                  "suspended": {
                    "summary": "License suspended",
                    "value": {
                      "message": "This license has been suspended."
                    }
                  },
                  "expired": {
                    "summary": "License expired",
                    "value": {
                      "message": "This license has expired."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "License key not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "License key not found."
                }
              }
            }
          },
          "422": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                },
                "example": {
                  "message": "The given data was invalid.",
                  "errors": {
                    "license_key": [
                      "The license key field is required."
                    ],
                    "machine_id": [
                      "The machine id field is required."
                    ]
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitResponse"
                },
                "example": {
                  "message": "Too many requests.",
                  "retry_after": 60
                }
              }
            }
          }
        }
      }
    },
    "/v1/license/deactivate": {
      "post": {
        "operationId": "deactivateLicense",
        "summary": "Deactivate a license",
        "description": "Removes a machine from a license's active devices. Frees up an activation slot.",
        "tags": [
          "License"
        ],
        "x-mint": {
          "title": "Deactivate License",
          "description": "Remove a machine from a license's active devices"
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeactivateRequest"
              },
              "example": {
                "license_key": "ABCDE-12345-FGHIJ-67890-KLMNO",
                "machine_id": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Machine deactivated successfully.",
            "headers": {
              "X-RateLimit-Limit": {
                "description": "The maximum number of requests allowed in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "description": "The number of requests remaining in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "description": "The Unix timestamp when the rate limit window resets.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeactivateResponse"
                },
                "example": {
                  "message": "Machine has been deactivated."
                }
              }
            }
          },
          "404": {
            "description": "License key not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "message": "License key not found."
                }
              }
            }
          },
          "422": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationErrorResponse"
                },
                "example": {
                  "message": "The given data was invalid.",
                  "errors": {
                    "license_key": [
                      "The license key field is required."
                    ],
                    "machine_id": [
                      "The machine id field is required."
                    ]
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitResponse"
                },
                "example": {
                  "message": "Too many requests.",
                  "retry_after": 60
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "LicenseKey": {
        "type": "string",
        "description": "License key in format XXXXX-XXXXX-XXXXX-XXXXX-XXXXX.",
        "pattern": "^[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}$",
        "example": "ABCDE-12345-FGHIJ-67890-KLMNO"
      },
      "MachineId": {
        "type": "string",
        "description": "SHA-256 hash of machine identifier (64 hex characters).",
        "pattern": "^[a-f0-9]{64}$",
        "example": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
      },
      "ActivateRequest": {
        "type": "object",
        "required": [
          "license_key",
          "machine_id",
          "machine_name",
          "app_version",
          "os_version"
        ],
        "properties": {
          "license_key": {
            "$ref": "#/components/schemas/LicenseKey"
          },
          "machine_id": {
            "$ref": "#/components/schemas/MachineId"
          },
          "machine_name": {
            "type": "string",
            "description": "Human-readable machine name.",
            "maxLength": 255,
            "example": "John's MacBook Pro"
          },
          "app_version": {
            "type": "string",
            "description": "Application version.",
            "maxLength": 50,
            "example": "0.2.0"
          },
          "os_version": {
            "type": "string",
            "description": "Operating system version.",
            "maxLength": 100,
            "example": "macOS 15.3"
          }
        }
      },
      "ValidateRequest": {
        "type": "object",
        "required": [
          "license_key",
          "machine_id"
        ],
        "properties": {
          "license_key": {
            "$ref": "#/components/schemas/LicenseKey"
          },
          "machine_id": {
            "$ref": "#/components/schemas/MachineId"
          }
        }
      },
      "DeactivateRequest": {
        "type": "object",
        "required": [
          "license_key",
          "machine_id"
        ],
        "properties": {
          "license_key": {
            "$ref": "#/components/schemas/LicenseKey"
          },
          "machine_id": {
            "$ref": "#/components/schemas/MachineId"
          }
        }
      },
      "LicenseData": {
        "type": "object",
        "description": "Core license information.",
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email address associated with the license.",
            "example": "john@example.com"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "License expiration date in ISO 8601 format.",
            "example": "2027-02-11T00:00:00+00:00"
          },
          "issued_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp when the license data was issued.",
            "example": "2026-02-12T10:30:00+00:00"
          },
          "license_key": {
            "$ref": "#/components/schemas/LicenseKey"
          },
          "status": {
            "type": "string",
            "description": "Current license status.",
            "enum": [
              "active",
              "suspended"
            ],
            "example": "active"
          }
        },
        "required": [
          "email",
          "expires_at",
          "issued_at",
          "license_key",
          "status"
        ]
      },
      "SignedLicenseResponse": {
        "type": "object",
        "description": "Signed license response containing license data and a cryptographic signature.",
        "properties": {
          "data": {
            "$ref": "#/components/schemas/LicenseData"
          },
          "signature": {
            "type": "string",
            "description": "Cryptographic signature of the license data for client-side verification.",
            "example": "base64-encoded-signature"
          }
        },
        "required": [
          "data",
          "signature"
        ]
      },
      "DeactivateResponse": {
        "type": "object",
        "description": "Response returned after successful machine deactivation.",
        "properties": {
          "message": {
            "type": "string",
            "example": "Machine has been deactivated."
          }
        },
        "required": [
          "message"
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "description": "Generic error response.",
        "properties": {
          "message": {
            "type": "string",
            "description": "Human-readable error message."
          }
        },
        "required": [
          "message"
        ]
      },
      "ValidationErrorResponse": {
        "type": "object",
        "description": "Validation error response with field-level error details.",
        "properties": {
          "message": {
            "type": "string",
            "description": "Summary error message.",
            "example": "The given data was invalid."
          },
          "errors": {
            "type": "object",
            "description": "Object where keys are field names and values are arrays of error messages.",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "example": {
              "license_key": [
                "The license key field is required."
              ]
            }
          }
        },
        "required": [
          "message",
          "errors"
        ]
      },
      "RateLimitResponse": {
        "type": "object",
        "description": "Response returned when the rate limit has been exceeded.",
        "properties": {
          "message": {
            "type": "string",
            "description": "Human-readable rate limit message.",
            "example": "Too many requests."
          },
          "retry_after": {
            "type": "integer",
            "description": "Number of seconds to wait before retrying.",
            "example": 60
          }
        },
        "required": [
          "message",
          "retry_after"
        ]
      }
    }
  }
}