{
  "openapi": "3.1.0",
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "info": {
    "title": "Fracture Shop Fulfillment API",
    "version": "1.0.0",
    "description": "Server-to-server reseller key fulfillment. Create and revoke API tokens in /reseller#integration. The full fsk_ token is shown once and is stored hashed. Credits are added manually by the owner. Current base credit prices before the assigned 40-75% reseller discount are Day 4.5, 3 Days 8, Week 15, Month 30, and Lifetime 100. Decimal *_credits fields are preferred. Legacy *_cents fields are integer hundredths of a credit, not fiat cents. Discounted unit prices are rounded upward to 0.01 credit before multiplication by quantity. Limits are 60 requests per token per minute and 120 per source IP per minute. Use stable paid-shop order references for idempotency. Example IDs and license keys are placeholders."
  },
  "servers": [
    {
      "url": "https://sentinelspoofs.xyz",
      "description": "Fracture production shop"
    },
    {
      "url": "https://sentinelspoofs-shop.jinxyware.workers.dev",
      "description": "Cloudflare deployment address"
    }
  ],
  "externalDocs": {
    "url": "https://sentinelspoofs.xyz/docs/api",
    "description": "Integration guide, token creation, retries, and server examples"
  },
  "security": [
    {
      "ResellerBearer": []
    }
  ],
  "tags": [
    {
      "name": "Credits"
    },
    {
      "name": "Catalog"
    },
    {
      "name": "Orders"
    }
  ],
  "paths": {
    "/api/v1/balance": {
      "get": {
        "operationId": "getResellerBalance",
        "tags": ["Credits"],
        "summary": "Read available credits and discount",
        "responses": {
          "200": {
            "description": "Available reseller credit balance.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Balance"
                },
                "example": {
                  "balance_credits": 100,
                  "balance_cents": 10000,
                  "discount_percent": 40,
                  "unit": "credits",
                  "credit_scale": 100
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, or revoked Bearer token, or inactive account.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Missing, invalid, or revoked Bearer token, or inactive account."
                }
              }
            }
          },
          "403": {
            "description": "The account is not eligible for reseller API access.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "The account is not eligible for reseller API access."
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry with the same order idempotency key when applicable.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "enum": ["60"]
                },
                "description": "Wait at least 60 seconds before retrying."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded. Retry with the same order idempotency key when applicable."
                }
              }
            }
          },
          "503": {
            "description": "Temporary service failure. Retry with backoff and the same order idempotency key when applicable.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Temporary service failure. Retry with backoff and the same order idempotency key when applicable."
                }
              }
            }
          },
          "500": {
            "description": "Unexpected service error. Retain the order reference when retrying.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Unexpected service error. Retain the order reference when retrying."
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/plans": {
      "get": {
        "operationId": "getResellerPlans",
        "tags": ["Catalog"],
        "summary": "Read active plans and discounted credit prices",
        "description": "Read this endpoint to discover current plan IDs and prices for the authenticated reseller. The sample contains one plan; the real response contains all active plans.",
        "responses": {
          "200": {
            "description": "Active plan catalog with reseller pricing.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plans"
                },
                "example": {
                  "plans": [
                    {
                      "id": "temp_1day",
                      "name": "1 Day",
                      "duration": "1day",
                      "duration_secs": 86400,
                      "price_credits": 4.5,
                      "price_cents": 450,
                      "discounted_price_credits": 2.7,
                      "discounted_price_cents": 270
                    }
                  ],
                  "unit": "credits",
                  "credit_scale": 100
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, or revoked Bearer token, or inactive account.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Missing, invalid, or revoked Bearer token, or inactive account."
                }
              }
            }
          },
          "403": {
            "description": "The account is not eligible for reseller API access.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "The account is not eligible for reseller API access."
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry with the same order idempotency key when applicable.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "enum": ["60"]
                },
                "description": "Wait at least 60 seconds before retrying."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded. Retry with the same order idempotency key when applicable."
                }
              }
            }
          },
          "503": {
            "description": "Temporary service failure. Retry with backoff and the same order idempotency key when applicable.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Temporary service failure. Retry with backoff and the same order idempotency key when applicable."
                }
              }
            }
          },
          "500": {
            "description": "Unexpected service error. Retain the order reference when retrying.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Unexpected service error. Retain the order reference when retrying."
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/orders": {
      "post": {
        "operationId": "createResellerOrder",
        "tags": ["Orders"],
        "summary": "Issue keys and debit credits atomically",
        "description": "Call from your shop server after verifying the payment event and paid state. The server resolves the account, price, and discount from the token. Use a stable Idempotency-Key derived from the paid shop order. Repeating the same key, plan, and quantity returns HTTP 200 with the original keys and replayed=true without another debit. Reusing it for different inputs returns HTTP 409. On ambiguous timeouts and temporary failures, retry the same request with the same key. Persist the returned order ID and keys and make your own customer delivery idempotent.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Stable unique server-side shop order reference. Preserve through retries. 8-128 letters, digits, underscores, dots, colons, or hyphens.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 128,
              "pattern": "^[a-zA-Z0-9_.:-]{8,128}$"
            },
            "example": "myshop:order:123456"
          }
        ],
        "requestBody": {
          "required": true,
          "description": "JSON body must be no more than 8,192 bytes. Plan and quantity are selected by the shop server after payment verification.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OrderRequest"
              },
              "example": {
                "plan_id": "temp_1day",
                "quantity": 1
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Keys issued, or the original matching order replayed with no additional debit.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Order"
                },
                "example": {
                  "id": "11111111-1111-4111-8111-111111111111",
                  "plan_id": "temp_1day",
                  "quantity": 1,
                  "keys": ["FRACTURE-XXXX-XXXX-XXXX-XXXX"],
                  "unit_price_credits": 2.7,
                  "unit_price_cents": 270,
                  "total_credits": 2.7,
                  "total_cents": 270,
                  "balance_credits": 97.3,
                  "balance_cents": 9730,
                  "replayed": false
                }
              }
            }
          },
          "400": {
            "description": "Invalid plan, quantity, or idempotency header.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid plan, quantity, or idempotency header."
                }
              }
            }
          },
          "402": {
            "description": "Insufficient reseller credits. Ask the owner for a manual top-up, then retry the same order.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Insufficient reseller credits. Ask the owner for a manual top-up, then retry the same order."
                }
              }
            }
          },
          "409": {
            "description": "The idempotency key was previously used with a different plan or quantity.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "The idempotency key was previously used with a different plan or quantity."
                }
              }
            }
          },
          "413": {
            "description": "Request body exceeds 8,192 bytes.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Request body exceeds 8,192 bytes."
                }
              }
            }
          },
          "415": {
            "description": "Content-Type must be application/json.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Content-Type must be application/json."
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, or revoked Bearer token, or inactive account.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Missing, invalid, or revoked Bearer token, or inactive account."
                }
              }
            }
          },
          "403": {
            "description": "The account is not eligible for reseller API access.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "The account is not eligible for reseller API access."
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry with the same order idempotency key when applicable.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              },
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "enum": ["60"]
                },
                "description": "Wait at least 60 seconds before retrying."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded. Retry with the same order idempotency key when applicable."
                }
              }
            }
          },
          "503": {
            "description": "Temporary service failure. Retry with backoff and the same order idempotency key when applicable.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Temporary service failure. Retry with backoff and the same order idempotency key when applicable."
                }
              }
            }
          },
          "500": {
            "description": "Unexpected service error. Retain the order reference when retrying.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "private, no-store"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Unexpected service error. Retain the order reference when retrying."
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ResellerBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "fsk_ followed by 64 lowercase hexadecimal characters",
        "description": "Create a token in the authenticated reseller portal. Send Authorization: Bearer YOUR_TOKEN. Store it in the shop server environment. Tokens authorize their owning reseller account only."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": ["error"]
      },
      "Balance": {
        "type": "object",
        "required": [
          "balance_credits",
          "balance_cents",
          "discount_percent",
          "unit",
          "credit_scale"
        ],
        "properties": {
          "balance_credits": {
            "type": "number",
            "minimum": 0,
            "multipleOf": 0.01,
            "description": "Decimal credits. One credit has 100 precision units."
          },
          "balance_cents": {
            "type": "integer",
            "minimum": 0,
            "description": "Legacy hundredths of one credit. Divide by 100 for credits."
          },
          "discount_percent": {
            "type": "integer",
            "minimum": 40,
            "maximum": 75
          },
          "unit": {
            "type": "string",
            "const": "credits"
          },
          "credit_scale": {
            "type": "integer",
            "const": 100
          }
        }
      },
      "Plan": {
        "type": "object",
        "required": [
          "id",
          "name",
          "duration",
          "duration_secs",
          "price_credits",
          "price_cents",
          "discounted_price_credits",
          "discounted_price_cents"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "temp_1day"
          },
          "name": {
            "type": "string",
            "example": "1 Day"
          },
          "duration": {
            "type": "string",
            "example": "1day"
          },
          "duration_secs": {
            "type": "integer",
            "minimum": 0,
            "description": "Zero denotes the Lifetime temporary-access license."
          },
          "price_credits": {
            "type": "number",
            "minimum": 0,
            "multipleOf": 0.01,
            "description": "Base credit price before the reseller discount."
          },
          "price_cents": {
            "type": "integer",
            "minimum": 0,
            "description": "Legacy hundredths of one credit. Divide by 100 for credits."
          },
          "discounted_price_credits": {
            "type": "number",
            "minimum": 0,
            "multipleOf": 0.01,
            "description": "Your discounted credit price per key, rounded upward to 0.01 credit."
          },
          "discounted_price_cents": {
            "type": "integer",
            "minimum": 0,
            "description": "Legacy hundredths of one credit. Divide by 100 for credits."
          }
        }
      },
      "Plans": {
        "type": "object",
        "required": ["plans", "unit", "credit_scale"],
        "properties": {
          "plans": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Plan"
            }
          },
          "unit": {
            "type": "string",
            "const": "credits"
          },
          "credit_scale": {
            "type": "integer",
            "const": 100
          }
        }
      },
      "OrderRequest": {
        "type": "object",
        "required": ["plan_id", "quantity"],
        "properties": {
          "plan_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 50,
            "description": "An active plan ID returned by the plans endpoint.",
            "example": "temp_1day"
          },
          "quantity": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500
          }
        }
      },
      "Order": {
        "type": "object",
        "required": [
          "id",
          "plan_id",
          "quantity",
          "keys",
          "unit_price_credits",
          "unit_price_cents",
          "total_credits",
          "total_cents",
          "balance_credits",
          "balance_cents",
          "replayed"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Fracture order ID. Persist it with your paid shop order."
          },
          "plan_id": {
            "type": "string",
            "example": "temp_1day"
          },
          "quantity": {
            "type": "integer",
            "minimum": 1,
            "maximum": 500
          },
          "keys": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "type": "string",
              "pattern": "^FRACTURE-[A-HJ-NP-Z2-9]{4}(?:-[A-HJ-NP-Z2-9]{4}){3}$"
            }
          },
          "unit_price_credits": {
            "type": "number",
            "minimum": 0,
            "multipleOf": 0.01,
            "description": "Decimal credits. One credit has 100 precision units."
          },
          "unit_price_cents": {
            "type": "integer",
            "minimum": 0,
            "description": "Legacy hundredths of one credit. Divide by 100 for credits."
          },
          "total_credits": {
            "type": "number",
            "minimum": 0,
            "multipleOf": 0.01,
            "description": "Decimal credits. One credit has 100 precision units."
          },
          "total_cents": {
            "type": "integer",
            "minimum": 0,
            "description": "Legacy hundredths of one credit. Divide by 100 for credits."
          },
          "balance_credits": {
            "type": "number",
            "minimum": 0,
            "multipleOf": 0.01,
            "description": "Current balance at response time. It may differ on a later replay."
          },
          "balance_cents": {
            "type": "integer",
            "minimum": 0,
            "description": "Legacy hundredths of one credit. Divide by 100 for credits."
          },
          "replayed": {
            "type": "boolean",
            "description": "True when the original order is returned without another debit."
          }
        }
      }
    }
  }
}
