{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/depixapp-com/json-schema/depixapp-com-verification-status-response-schema.json",
  "title": "VerificationStatusResponse",
  "description": "Whether this account can use the merchant tools, and what is still missing if it cannot. Verification is independent of how much the account may receive — it never changes a limit.",
  "x-generated": "2026-11-05",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/depixapp-com-openapi.yml#/components/schemas/VerificationStatusResponse",
  "type": "object",
  "required": [
    "verified",
    "verified_at",
    "whatsapp_verified",
    "method",
    "enabled",
    "eligible",
    "requirements",
    "progress",
    "remaining",
    "missing",
    "unlocks",
    "lbtc_fuel",
    "steps"
  ],
  "additionalProperties": false,
  "properties": {
    "verified": {
      "type": "boolean",
      "description": "True when the merchant tools are unlocked on this account."
    },
    "verified_at": {
      "type": [
        "string",
        "null"
      ],
      "description": "When verification completed (RFC 3339 UTC, \"YYYY-MM-DDTHH:MM:SS.sssZ\"). Null while unverified."
    },
    "whatsapp_verified": {
      "type": "integer",
      "description": "1 once the account has verified its WhatsApp number, 0 otherwise (raw SQLite boolean). It gates the first deposit — a human account must clear it in the app before verifying. Always 0 for an agent account, which is exempt (it proves a domain instead).",
      "enum": [
        0,
        1
      ]
    },
    "method": {
      "type": "string",
      "description": "Which proof applies to this account. `round_trip`: send money from a CPF/CNPJ to the account and withdraw it back to the SAME document. `domain`: prove control of a registrable domain by DNS TXT (agent accounts — POST /api/agents/verify-domain).",
      "enum": [
        "round_trip",
        "domain"
      ]
    },
    "enabled": {
      "type": "boolean",
      "description": "False when automatic verification is switched off platform-wide; contact support instead of retrying."
    },
    "eligible": {
      "type": "boolean",
      "description": "True when every requirement is satisfied and POST /api/verification would promote the account. A promotion still has to land: a suspended account never verifies."
    },
    "requirements": {
      "type": "object",
      "description": "What this account's proof requires. Null members do not apply to the account's method.",
      "required": [
        "deposit_cents",
        "withdraw_cents",
        "min_account_age_days",
        "max_days_between_legs",
        "domain_proof"
      ],
      "additionalProperties": false,
      "properties": {
        "deposit_cents": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Minimum to receive from one document, in cents. Null on the domain method."
        },
        "withdraw_cents": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Minimum to withdraw back to that SAME document, in cents. Lower than deposit_cents because fees are deducted along the way. Null on the domain method."
        },
        "min_account_age_days": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Minimum account age in days. Null on the domain method. **0 means there is no age requirement at all** — the `account_age` step is then omitted from `steps` and never appears in `missing`. 0 is the value in force."
        },
        "max_days_between_legs": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Maximum days between the two movements — they must sit inside one window of this length. Null on the domain method."
        },
        "domain_proof": {
          "type": "boolean",
          "description": "True when a proven domain is what verifies this account."
        }
      }
    },
    "progress": {
      "type": "object",
      "description": "What has been done so far. Amounts count only movements made after this feature went live.",
      "required": [
        "account_age_days",
        "account_age_ok",
        "deposited_cents",
        "withdrawn_cents",
        "deposit_leg_ok",
        "withdraw_leg_ok",
        "tax_number",
        "tax_number_locked",
        "domain"
      ],
      "additionalProperties": false,
      "properties": {
        "account_age_days": {
          "type": "integer",
          "description": "Age of the account in whole days."
        },
        "account_age_ok": {
          "type": "boolean",
          "description": "True when the age requirement is satisfied."
        },
        "deposited_cents": {
          "type": "integer",
          "description": "Received from the best-matching document, in cents."
        },
        "withdrawn_cents": {
          "type": "integer",
          "description": "Withdrawn back to that same document, in cents."
        },
        "deposit_leg_ok": {
          "type": "boolean",
          "description": "True when the received side reaches its minimum."
        },
        "withdraw_leg_ok": {
          "type": "boolean",
          "description": "True when the withdrawn side reaches its minimum."
        },
        "tax_number": {
          "type": [
            "string",
            "null"
          ],
          "description": "The document being matched, MASKED (only the last digits are shown). Null when no document has been seen yet."
        },
        "tax_number_locked": {
          "type": "boolean",
          "description": "True once a document is permanently bound to this account. A document verifies exactly one account, so a bound one can never verify another."
        },
        "domain": {
          "type": [
            "string",
            "null"
          ],
          "description": "The proven domain, on the domain method. Null otherwise."
        }
      }
    },
    "remaining": {
      "type": "object",
      "description": "How much is left on each requirement — zero when satisfied, null when it does not apply.",
      "required": [
        "deposit_cents",
        "withdraw_cents",
        "account_age_days"
      ],
      "additionalProperties": false,
      "properties": {
        "deposit_cents": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Still to receive from one document, in cents."
        },
        "withdraw_cents": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Still to withdraw back to it, in cents."
        },
        "account_age_days": {
          "type": [
            "integer",
            "null"
          ],
          "description": "Days the account still has to wait."
        }
      }
    },
    "missing": {
      "type": "array",
      "description": "Machine-readable list of what is blocking verification — empty when eligible. Branch on these instead of parsing prose.",
      "items": {
        "type": "string",
        "description": "`account_age`: the account is too young (never reported while `requirements.min_account_age_days` is 0). `deposit_leg`: not enough received from one document. `withdraw_leg`: not enough withdrawn back to it (also reported when both sides are met but too far apart in time). `domain_proof`: no proven domain yet. `account_blocked`: the account is suspended. `tax_number_in_use`: that document already verified another account. `verification_disabled`: automatic verification is switched off.",
        "enum": [
          "verification_disabled",
          "account_age",
          "deposit_leg",
          "withdraw_leg",
          "domain_proof",
          "account_blocked",
          "tax_number_in_use"
        ]
      }
    },
    "unlocks": {
      "type": "array",
      "description": "What verification unlocks on this account.",
      "items": {
        "type": "string",
        "description": "A capability name."
      }
    },
    "lbtc_fuel": {
      "type": [
        "object",
        "null"
      ],
      "description": "How much DePix to convert to L-BTC before withdrawing, and null when it does not apply (agent accounts). The Liquid network charges its transaction fee in L-BTC, so a wallet holding only DePix cannot move its own money — the most common reason a first withdrawal never leaves. This is GUIDANCE, not a requirement: it is absent from `requirements` and never affects `eligible`, because a non-custodial wallet's balance is not visible server-side.",
      "required": [
        "min_depix_cents",
        "balance_known",
        "sufficient"
      ],
      "additionalProperties": false,
      "properties": {
        "min_depix_cents": {
          "type": "integer",
          "description": "Minimum DePix to convert to L-BTC, in cents. Convert about this much — converting a lot more can leave too little DePix to complete the withdrawal step."
        },
        "balance_known": {
          "type": "boolean",
          "description": "Whether the server knows this account's L-BTC balance. Always false: the wallet is non-custodial and no balance is reported to the server. Determine it in the wallet."
        },
        "sufficient": {
          "type": [
            "boolean",
            "null"
          ],
          "description": "Whether the account already holds enough L-BTC. Always null, for the reason in balance_known — treat null as \"unknown\", never as \"no\"."
        }
      }
    },
    "steps": {
      "type": "array",
      "description": "The checklist IN THE ORDER THE ACCOUNT MUST ACT — render it as given and do not reorder or recompute it. `convert_lbtc` comes before `withdraw` because the withdrawal cannot be broadcast without network fuel. Amounts and days are pre-computed per step so the client never restates the rule.",
      "items": {
        "type": "object",
        "description": "One step of the checklist.",
        "required": [
          "id",
          "state",
          "target_cents",
          "remaining_cents",
          "target_days",
          "remaining_days"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "`deposit`: receive from your own CPF/CNPJ. `convert_lbtc`: convert a little DePix to L-BTC for the network fee. `withdraw`: send DePix back to the same document. `account_age`: the account also has to be old enough — PRESENT ONLY when `requirements.min_account_age_days` > 0, which it is not today. `domain_proof`: agent accounts — replaces all of the above. Render the list as given; a step that is absent is not a requirement.",
            "enum": [
              "deposit",
              "convert_lbtc",
              "withdraw",
              "account_age",
              "domain_proof"
            ]
          },
          "state": {
            "type": "string",
            "description": "`done`, `pending`, or `unknown` — `unknown` means the server genuinely cannot tell (the L-BTC step, since no wallet balance reaches it) and the client must resolve it locally. Never render `unknown` as a failure.",
            "enum": [
              "done",
              "pending",
              "unknown"
            ]
          },
          "target_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "What this step asks for, in cents. Null when the step is not about an amount."
          },
          "remaining_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How much of that is left, in cents. Null when the step is not about an amount, or when the server cannot know."
          },
          "target_days": {
            "type": [
              "integer",
              "null"
            ],
            "description": "What this step asks for, in days. Null when the step is not about time."
          },
          "remaining_days": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How many days are left. Null when the step is not about time."
          }
        }
      }
    }
  }
}