{
  "openapi": "3.0.3",
  "info": {
    "title": "Makatea API",
    "version": "2026-09-23",
    "description": "The Makatea API lets your system create members and billing orders, register payments, and\nreceive events. Every response carries `Makatea-API-Version: 2026-09-23` and an\n`X-Request-Id` (send your own to correlate logs).\n\n## Authentication\n\nCreate an API key in **Makatea → Settings → API**. Send it in either header:\n\n    Authorization: Bearer mk_live_…\n    X-API-Key: mk_live_…\n\nKeys have scopes: `read` (GET) and `write` (everything else). A session JWT from the Makatea app\nis also accepted in `Authorization` (legacy). Missing or invalid credentials → `401`; a key\nwithout the needed scope → `403 insufficient_scope`.\n\n## Responses\n\nSuccess always has the same shape, for single objects and for lists:\n\n    {\"data\": {…} }\n    {\"data\": [ … ], \"pagination\": {\"limit\": 100, \"has_more\": true, \"next_cursor\": \"eyJm…\"}}\n\nErrors always look like this:\n\n    {\"error\": {\"code\": \"validation_error\", \"message\": \"amount must be a positive number\", \"request_id\": \"…\"}}\n\n| Status | Meaning |\n|---|---|\n| 400 | Malformed request: invalid JSON, unknown query parameter (`unknown_parameter` lists the allowed ones), bad cursor |\n| 401 | Missing/invalid credentials |\n| 403 | Key lacks the scope |\n| 404 | Not found (also unknown routes: `route_not_found`) |\n| 405 | Route exists with another method |\n| 409 | Conflict with the current state (already paid, duplicate `external_ref`, opted out, …) |\n| 413 | Body or document too large |\n| 422 | Validation error, or an `Idempotency-Key` reused with a different body |\n| 429 | Rate limit exceeded (see `Retry-After`) |\n| 5xx | Our fault; safe to retry with the same `Idempotency-Key` |\n\n## Pagination and sync\n\nLists accept `limit` (default 100, max 500) and `cursor`. Pass the `next_cursor` you received to\nget the next page, keeping the same filters; stop when `has_more` is `false`. `offset` still\nworks for compatibility but can skip rows while data changes. Add `count=true` to get\n`pagination.total` (slower).\n\nTo mirror data into your system, call the list with `updated_since=<last sync instant>`: results come\nordered by `updated_at` ascending, so you can page with the cursor and store the `updated_at` of the\nlast row as your next checkpoint.\n\n## Idempotency\n\nSend `Idempotency-Key: <unique string>` on any POST/PUT/PATCH/DELETE. Retrying with the same key and\nthe same body returns the original response (with header `Idempotent-Replayed: true`) instead of\nacting twice. The same key with a different body is `422 idempotency_key_reused`; while the first\nrequest is still running, `409 idempotency_in_progress`. Keys are kept 24 hours.\n\n## Webhooks\n\nRegister an https endpoint (`POST /v1/webhook-endpoints`) with the event types you want (or `*`).\nThe response includes the signing `secret` (`whsec_…`) **only once**. Each event is POSTed as:\n\n    {\"id\": \"<event id>\", \"type\": \"payment.received\", \"created_at\": \"…\", \"data\": {…}}\n\nwith headers `Makatea-Event-Id`, `Makatea-Event-Type` and\n\n    Makatea-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, \"<t>.<raw body>\")>\n\nVerify by recomputing the HMAC over `t + \".\" + raw body` and comparing in constant time; reject\ntimestamps older than 5 minutes. Answer 2xx within 10 seconds. Otherwise we retry after 1 min, 5 min,\n30 min, 2 h, 6 h and 24 h; after 8 failed attempts the delivery is `failed` (you can retry it with\n`POST /v1/webhook-deliveries/{id}/retry`). Deliveries can arrive more than once or out of order: use\nthe event `id` to de-duplicate.\n\nEvent types: `member.created`, `member.updated`, `billing_order.created`, `billing_order.updated`, `billing_order.paid`, `billing_order.cancelled`, `payment.received`, `payment.voided`, `promise.created`, `promise.kept`, `promise.broken`, `message.sent`, `message.received`. `*.updated` events carry\n`{\"object\": {…}, \"previous_attributes\": {…}}`. Records created by CSV imports do not emit events.\n\n## Limits\n\n600 requests per minute per API key (per organization for session JWTs). Every response includes\n`X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (unix seconds). Over the limit →\n`429` with `Retry-After`. Request bodies up to 21 MB; documents up to 15 MB.\n\n## Status\n\n`GET /v1/status` (no key) says whether the service and its database answer."
  },
  "servers": [
    {
      "url": "https://makatea.ai/api",
      "description": "Producción"
    }
  ],
  "security": [
    {
      "ApiKey": []
    },
    {
      "Bearer": []
    }
  ],
  "tags": [
    {
      "name": "Status"
    },
    {
      "name": "Members"
    },
    {
      "name": "Promises"
    },
    {
      "name": "Credit"
    },
    {
      "name": "Messages"
    },
    {
      "name": "Documents"
    },
    {
      "name": "Billing orders"
    },
    {
      "name": "Payments"
    },
    {
      "name": "Credits (loans)"
    },
    {
      "name": "Webhooks"
    },
    {
      "name": "Operations"
    }
  ],
  "paths": {
    "/v1/status": {
      "get": {
        "tags": [
          "Status"
        ],
        "summary": "Service status (no authentication)",
        "operationId": "get_v1_status",
        "description": "Checks that the database answers. Returns 503 when it does not.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Status"
                    }
                  }
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/v1/members": {
      "get": {
        "tags": [
          "Members"
        ],
        "summary": "List members",
        "operationId": "get_v1_members",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "contact",
                "active",
                "at_risk",
                "delinquent",
                "dormant",
                "churned"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Search in name or phone."
          },
          {
            "name": "external_ref",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Exact match on your own identifier."
          },
          {
            "name": "updated_since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only records changed at or after this instant (ISO-8601). Switches ordering to `updated_at` ascending, so you can resume a sync with the last `next_cursor`."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "`next_cursor` from the previous page."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Legacy; prefer `cursor`."
          },
          {
            "name": "count",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Include `pagination.total`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Member"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Members"
        ],
        "summary": "Create a member",
        "operationId": "post_v1_members",
        "description": "Two members may share a phone number. `external_ref` is unique per organization (409 if reused).",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "full_name"
                ],
                "properties": {
                  "full_name": {
                    "type": "string"
                  },
                  "phone_e164": {
                    "type": "string",
                    "example": "+5215512345678"
                  },
                  "email": {
                    "type": "string"
                  },
                  "state": {
                    "type": "string",
                    "enum": [
                      "contact",
                      "active",
                      "at_risk",
                      "delinquent",
                      "dormant",
                      "churned"
                    ],
                    "default": "active"
                  },
                  "external_ref": {
                    "type": "string",
                    "description": "Your identifier for this member."
                  },
                  "identity": {
                    "type": "object",
                    "description": "Free-form attributes (used as template variables)."
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "notes": {
                    "type": "string"
                  },
                  "customer_since": {
                    "type": "string",
                    "format": "date",
                    "description": "When the member became your customer (not when it entered Makatea). Not in the future."
                  },
                  "do_not_contact": {
                    "type": "boolean",
                    "default": false,
                    "description": "Never send this member any message. Requires `do_not_contact_reason`."
                  },
                  "do_not_contact_reason": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Member"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/members/{id}": {
      "get": {
        "tags": [
          "Members"
        ],
        "summary": "Get a member (with billing orders and recent interactions)",
        "operationId": "get_v1_members_id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Member"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Members"
        ],
        "summary": "Update a member",
        "operationId": "put_v1_members_id",
        "description": "Only the fields you send are changed.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "full_name": {
                    "type": "string"
                  },
                  "phone_e164": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "nullable": true
                  },
                  "state": {
                    "type": "string",
                    "enum": [
                      "contact",
                      "active",
                      "at_risk",
                      "delinquent",
                      "dormant",
                      "churned"
                    ]
                  },
                  "external_ref": {
                    "type": "string",
                    "nullable": true
                  },
                  "identity": {
                    "type": "object"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "notes": {
                    "type": "string",
                    "nullable": true
                  },
                  "customer_since": {
                    "type": "string",
                    "format": "date",
                    "nullable": true,
                    "description": "Not in the future. `null` clears it."
                  },
                  "do_not_contact": {
                    "type": "boolean",
                    "description": "`true` requires `do_not_contact_reason` (sent now or already on file); `false` clears the reason. Pending messages are withdrawn when it turns on."
                  },
                  "do_not_contact_reason": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Can be sent alone to change the reason of a member already marked."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Member"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Members"
        ],
        "summary": "Update a member (partial)",
        "operationId": "patch_v1_members_id",
        "description": "Only the fields you send are changed.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "full_name": {
                    "type": "string"
                  },
                  "phone_e164": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "nullable": true
                  },
                  "state": {
                    "type": "string",
                    "enum": [
                      "contact",
                      "active",
                      "at_risk",
                      "delinquent",
                      "dormant",
                      "churned"
                    ]
                  },
                  "external_ref": {
                    "type": "string",
                    "nullable": true
                  },
                  "identity": {
                    "type": "object"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "notes": {
                    "type": "string",
                    "nullable": true
                  },
                  "customer_since": {
                    "type": "string",
                    "format": "date",
                    "nullable": true,
                    "description": "Not in the future. `null` clears it."
                  },
                  "do_not_contact": {
                    "type": "boolean",
                    "description": "`true` requires `do_not_contact_reason` (sent now or already on file); `false` clears the reason. Pending messages are withdrawn when it turns on."
                  },
                  "do_not_contact_reason": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Can be sent alone to change the reason of a member already marked."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Member"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Members"
        ],
        "summary": "Soft-delete a member (state becomes `churned`)",
        "operationId": "delete_v1_members_id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Member"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/members/{id}/promises": {
      "post": {
        "tags": [
          "Promises"
        ],
        "summary": "Record a payment promise",
        "operationId": "post_v1_members_id_promises",
        "description": "Pauses collection messages until the promised date (one reminder survives). Replaces any active promise. When the date passes the promise becomes `kept` or `broken` (webhooks `promise.kept` / `promise.broken`).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "promised_date"
                ],
                "properties": {
                  "promised_date": {
                    "type": "string",
                    "format": "date"
                  },
                  "amount": {
                    "type": "number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PromiseState"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Promises"
        ],
        "summary": "Current promise and history",
        "operationId": "get_v1_members_id_promises",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PromiseState"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/members/{id}/promises/current": {
      "delete": {
        "tags": [
          "Promises"
        ],
        "summary": "Cancel the active promise",
        "operationId": "delete_v1_members_id_promises_current",
        "description": "Marks it `cancelled` (kept in history) and re-schedules collection for the member's open orders. 409 if there is no active promise.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PromiseState"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/members/{id}/credit": {
      "get": {
        "tags": [
          "Credit"
        ],
        "summary": "Credit balance and its ledger",
        "operationId": "get_v1_members_id_credit",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Credit"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/members/{id}/credit/apply": {
      "post": {
        "tags": [
          "Credit"
        ],
        "summary": "Apply the credit balance to open orders (oldest first)",
        "operationId": "post_v1_members_id_credit_apply",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/CreditApplied"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/members/{id}/conversation": {
      "get": {
        "tags": [
          "Messages"
        ],
        "summary": "Messages exchanged with a member (newest first)",
        "operationId": "get_v1_members_id_conversation",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "channel",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "whatsapp",
                "sms",
                "email"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "`next_cursor` from the previous page."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Legacy; prefer `cursor`."
          },
          {
            "name": "count",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Include `pagination.total`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Interaction"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/members/{id}/documents": {
      "post": {
        "tags": [
          "Documents"
        ],
        "summary": "Attach a document to a member",
        "operationId": "post_v1_members_id_documents",
        "description": "Send the file inline (`content_base64`) or a public https `url` the server downloads. Max 15 MB. With `external_ref`, repeating the call returns the existing document (200) instead of uploading twice.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "mime_type": {
                    "type": "string"
                  },
                  "content_base64": {
                    "type": "string",
                    "format": "byte"
                  },
                  "url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "external_ref": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "ine_frontal",
                      "ine_reverso",
                      "comprobante",
                      "otro"
                    ],
                    "default": "otro"
                  },
                  "description": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Document"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Documents"
        ],
        "summary": "List a member's documents (with temporary download URLs, valid 1 hour)",
        "operationId": "get_v1_members_id_documents",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "external_ref",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Exact match on your own identifier."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Document"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/members/{id}/documents/{document_id}": {
      "delete": {
        "tags": [
          "Documents"
        ],
        "summary": "Delete a document (file and record)",
        "operationId": "delete_v1_members_id_documents_document_id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "document_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Deleted"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing-orders": {
      "get": {
        "tags": [
          "Billing orders"
        ],
        "summary": "List billing orders",
        "operationId": "get_v1_billing_orders",
        "description": "Default order: `due_date` ascending. With `updated_since`: `updated_at` ascending.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "overdue",
                "paid",
                "cancelled"
              ]
            }
          },
          {
            "name": "member_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "external_ref",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Exact match on your own identifier."
          },
          {
            "name": "updated_since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only records changed at or after this instant (ISO-8601). Switches ordering to `updated_at` ascending, so you can resume a sync with the last `next_cursor`."
          },
          {
            "name": "credit_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Only the installments of this credit."
          },
          {
            "name": "due_from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "due_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "`next_cursor` from the previous page."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Legacy; prefer `cursor`."
          },
          {
            "name": "count",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Include `pagination.total`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BillingOrder"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Billing orders"
        ],
        "summary": "Create a billing order",
        "operationId": "post_v1_billing_orders",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "member_id",
                  "amount",
                  "due_date"
                ],
                "properties": {
                  "member_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "amount": {
                    "type": "number"
                  },
                  "currency": {
                    "type": "string",
                    "default": "MXN"
                  },
                  "due_date": {
                    "type": "string",
                    "format": "date"
                  },
                  "period_label": {
                    "type": "string"
                  },
                  "external_ref": {
                    "type": "string",
                    "description": "Unique per organization."
                  },
                  "notes": {
                    "type": "string"
                  },
                  "capital_portion": {
                    "type": "number",
                    "nullable": true,
                    "description": "Principal part of this installment. Send together with `interest_portion` (both `null` removes the breakdown). capital + interest ≤ amount; the rest of `amount` is the collectible late fee and, beyond it, «other charges»."
                  },
                  "interest_portion": {
                    "type": "number",
                    "nullable": true,
                    "description": "Interest part of this installment (include commissions you consider interest)."
                  },
                  "credit_id": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Credit (loan) this installment belongs to; must be of the same member. `null` detaches it."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BillingOrder"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing-orders/{id}": {
      "get": {
        "tags": [
          "Billing orders"
        ],
        "summary": "Get a billing order",
        "operationId": "get_v1_billing_orders_id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BillingOrder"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Billing orders"
        ],
        "summary": "Update a billing order (move the date, change the amount, …)",
        "operationId": "patch_v1_billing_orders_id",
        "description": "Moving `due_date` re-schedules its collection messages. Raising `amount` above what was paid re-opens a paid order; lowering it to what is covered marks it paid. `late_fee` sets the accumulated late fee (absolute). `capital_portion`/`interest_portion` set the breakdown (also on paid orders: it is history) and re-apply the order's payments. Cancelled orders cannot be edited (409).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "due_date": {
                    "type": "string",
                    "format": "date"
                  },
                  "amount": {
                    "type": "number"
                  },
                  "period_label": {
                    "type": "string",
                    "nullable": true
                  },
                  "late_fee": {
                    "type": "number"
                  },
                  "external_ref": {
                    "type": "string",
                    "nullable": true
                  },
                  "notes": {
                    "type": "string",
                    "nullable": true
                  },
                  "member_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Move the order (with its payments) to another member of the organization."
                  },
                  "capital_portion": {
                    "type": "number",
                    "nullable": true,
                    "description": "Principal part of this installment. Send together with `interest_portion` (both `null` removes the breakdown). capital + interest ≤ amount; the rest of `amount` is the collectible late fee and, beyond it, «other charges»."
                  },
                  "interest_portion": {
                    "type": "number",
                    "nullable": true,
                    "description": "Interest part of this installment (include commissions you consider interest)."
                  },
                  "credit_id": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Credit (loan) this installment belongs to; must be of the same member. `null` detaches it."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BillingOrder"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Billing orders"
        ],
        "summary": "Cancel a billing order (409 if already paid)",
        "operationId": "delete_v1_billing_orders_id",
        "description": "Optional `reason` (query `?reason=` or JSON body `{\"reason\": …}`, max 500 chars) is stored as `cancel_reason` — e.g. «consolidated into another account». Cancelling an already cancelled order with a reason updates the reason.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "reason",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 500
            },
            "description": "Why the order is cancelled (stored as `cancel_reason`)."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BillingOrder"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing-orders/{id}/pay": {
      "post": {
        "tags": [
          "Payments"
        ],
        "summary": "Settle the whole remaining balance",
        "operationId": "post_v1_billing_orders_id_pay",
        "description": "Registers one payment for exactly the remaining balance. For partial or historical payments use `POST /v1/billing-orders/{id}/payments`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "method": {
                    "type": "string",
                    "default": "external"
                  },
                  "type": {
                    "type": "string",
                    "deprecated": true,
                    "description": "Old name of `method`."
                  },
                  "reference": {
                    "type": "string"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "paid_at": {
                    "type": "string",
                    "description": "When the money was received. Defaults to now. A date without time (`2026-09-22`) means that day in the organization's timezone (`organizations.timezone`): today → now, any other day → 12:00 local that day, so it never shifts to the previous day. A date and time without offset (`2026-09-22T23:30`) is that wall-clock time in the organization's timezone. A full ISO-8601 timestamp with `Z` or an offset is taken as is. Cannot be in the future.",
                    "example": "2026-09-22"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PaymentResult"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing-orders/{id}/payments": {
      "post": {
        "tags": [
          "Payments"
        ],
        "summary": "Register a payment (partial, historical date, overpayment policy)",
        "operationId": "post_v1_billing_orders_id_payments",
        "description": "`excess` decides what happens when `amount` is larger than the balance: `reject` (default) answers 422 `amount_exceeds_balance`; `credit` applies the excess to the member's next open orders (oldest first) and keeps the rest as credit balance.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount"
                ],
                "properties": {
                  "amount": {
                    "type": "number"
                  },
                  "paid_at": {
                    "type": "string",
                    "description": "When the money was received. Defaults to now. A date without time (`2026-09-22`) means that day in the organization's timezone (`organizations.timezone`): today → now, any other day → 12:00 local that day, so it never shifts to the previous day. A date and time without offset (`2026-09-22T23:30`) is that wall-clock time in the organization's timezone. A full ISO-8601 timestamp with `Z` or an offset is taken as is. Cannot be in the future.",
                    "example": "2026-09-22"
                  },
                  "method": {
                    "type": "string",
                    "example": "transfer"
                  },
                  "reference": {
                    "type": "string"
                  },
                  "notes": {
                    "type": "string"
                  },
                  "excess": {
                    "type": "string",
                    "enum": [
                      "reject",
                      "credit"
                    ],
                    "default": "reject"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PaymentResult"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing-orders/{id}/forgive": {
      "post": {
        "tags": [
          "Billing orders"
        ],
        "summary": "Forgive (condone) part or all of the balance",
        "operationId": "post_v1_billing_orders_id_forgive",
        "description": "Forgiven money is not a payment: it does not appear in `/v1/payments`. Without `amount`, forgives the whole balance and the order becomes `paid`. On orders with a breakdown the forgiveness takes, of what is still owed, late fee first, then interest, then other charges and principal last (`forgiven_now_breakdown`; forgiven principal is a loss).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "number"
                  },
                  "reason": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BillingOrder"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing-orders/{id}/late-fees": {
      "post": {
        "tags": [
          "Billing orders"
        ],
        "summary": "Add a late fee to an order",
        "operationId": "post_v1_billing_orders_id_late_fees",
        "description": "Adds to `late_fee` and fires the `late_fee_applied` notice if the organization configured one. With `collectible: true` the fee is also added to `amount` (it becomes owed; a paid order re-opens). When omitted, the organization's `moratorio_cobrable` setting decides (default: informational only).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount"
                ],
                "properties": {
                  "amount": {
                    "type": "number"
                  },
                  "collectible": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/BillingOrder"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing-orders/{id}/payment-link": {
      "post": {
        "tags": [
          "Billing orders"
        ],
        "summary": "Create (or reuse) a payment link with the organization's payment provider",
        "operationId": "post_v1_billing_orders_id_payment_link",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/PaymentLink"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/credits": {
      "get": {
        "tags": [
          "Credits (loans)"
        ],
        "summary": "List credits (loans) with their principal, what came back and what is owed",
        "operationId": "get_v1_credits",
        "description": "Each credit groups the installments of one loan (`external_ref` = your folio) and carries its principal. `totals` are to date: `capital_recovered`/`interest_collected`/`late_fee_collected` = what the payment ledger applied to its installments (voided payments net out); `capital_outstanding`/`interest_outstanding` = of its open installments with breakdown; `balance` = amount − paid − forgiven of its open installments; `paid_without_ledger` = paid amounts imported as history, with no ledger entry. Default order: `start_date` descending; with `updated_since`: `updated_at` ascending.",
        "parameters": [
          {
            "name": "member_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "external_ref",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Exact match on your own identifier."
          },
          {
            "name": "updated_since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only records changed at or after this instant (ISO-8601). Switches ordering to `updated_at` ascending, so you can resume a sync with the last `next_cursor`."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "liquidated",
                "cancelled"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "`next_cursor` from the previous page."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Legacy; prefer `cursor`."
          },
          {
            "name": "count",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Include `pagination.total`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Loan"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Credits (loans)"
        ],
        "summary": "Create a credit (loan) and attach its installments",
        "operationId": "post_v1_credits",
        "description": "`external_ref` (your folio) is unique per organization: reusing it is 409. Installments are attached by id or by their `external_ref`; they must belong to the same member (422 `credit_member_mismatch`). `start_date` is when the money was lent: it decides in which period the principal counts as «placed».",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "member_id",
                  "principal",
                  "start_date"
                ],
                "properties": {
                  "member_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "external_ref": {
                    "type": "string",
                    "description": "Your folio / contract number. Unique per organization."
                  },
                  "principal": {
                    "type": "number",
                    "description": "Money lent (capital prestado)."
                  },
                  "start_date": {
                    "type": "string",
                    "format": "date",
                    "description": "Date the money was lent."
                  },
                  "total_amount": {
                    "type": "number",
                    "description": "Total the member will pay (principal + interest), if you have it."
                  },
                  "periods": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 600
                  },
                  "frequency": {
                    "type": "string",
                    "enum": [
                      "weekly",
                      "biweekly",
                      "monthly",
                      "daily",
                      "other"
                    ]
                  },
                  "notes": {
                    "type": "string"
                  },
                  "billing_order_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  },
                  "billing_order_external_refs": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LoanDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/credits/{id}": {
      "get": {
        "tags": [
          "Credits (loans)"
        ],
        "summary": "Get a credit with its installments and their breakdown",
        "operationId": "get_v1_credits_id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LoanDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Credits (loans)"
        ],
        "summary": "Update a credit, attach or detach installments",
        "operationId": "patch_v1_credits_id",
        "description": "Only the fields you send change. `status` can be set to `cancelled` (the principal stops counting as placed) or back to `active`; `liquidated` is computed from its installments.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "external_ref": {
                    "type": "string",
                    "nullable": true
                  },
                  "principal": {
                    "type": "number"
                  },
                  "start_date": {
                    "type": "string",
                    "format": "date"
                  },
                  "total_amount": {
                    "type": "number",
                    "nullable": true
                  },
                  "periods": {
                    "type": "integer",
                    "nullable": true
                  },
                  "frequency": {
                    "type": "string",
                    "enum": [
                      "weekly",
                      "biweekly",
                      "monthly",
                      "daily",
                      "other"
                    ],
                    "nullable": true
                  },
                  "notes": {
                    "type": "string",
                    "nullable": true
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "cancelled"
                    ]
                  },
                  "add_billing_order_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  },
                  "add_billing_order_external_refs": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "remove_billing_order_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/LoanDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/settings/payment-allocation": {
      "get": {
        "tags": [
          "Credits (loans)"
        ],
        "summary": "How payments are split between late fee, interest and principal",
        "operationId": "get_v1_settings_payment_allocation",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AllocationRule"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Credits (loans)"
        ],
        "summary": "Change the payment allocation rule",
        "operationId": "put_v1_settings_payment_allocation",
        "description": "`moratorio_interes_capital` (default: late fee, then interest, then other charges, then principal), `proporcional` (each payment split in proportion to what is owed of each part) or `al_liquidar` (a partial payment stays «unassigned» until the installment is settled; the settling payment recognizes all of its principal and interest on its date). The rule is fixed on each installment when its breakdown is loaded; with `apply_existing: true` every installment with breakdown is re-applied with the new rule (past figures change).",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rule"
                ],
                "properties": {
                  "rule": {
                    "type": "string",
                    "enum": [
                      "moratorio_interes_capital",
                      "proporcional",
                      "al_liquidar"
                    ]
                  },
                  "apply_existing": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AllocationRule"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/payments": {
      "get": {
        "tags": [
          "Payments"
        ],
        "summary": "List payments (the ledger)",
        "operationId": "get_v1_payments",
        "description": "Every money movement: payments (`kind: payment`) and reversals of voided payments (`kind: reversal`, negative amount). `allocation` says how each entry was applied to its installment (principal, interest, late fee, other charges, unassigned; `null` when the installment has no breakdown); a reversal carries exactly the same figures, negative. `paid_at` is when the money was received (it can be in the past); `recorded_at` is when the entry reached Makatea — `created_since`/`updated_since` and the ordering use `recorded_at`, so a back-dated payment registered today still shows up in your next sync. The ledger is append-only, so `updated_since` is an alias of `created_since`. Default order: newest first; with a `*_since` filter: oldest first.",
        "parameters": [
          {
            "name": "member_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "billing_order_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "created_since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only records created at or after this instant (ISO-8601). Ordering becomes `created_at` ascending."
          },
          {
            "name": "updated_since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only records changed at or after this instant (ISO-8601). Switches ordering to `updated_at` ascending, so you can resume a sync with the last `next_cursor`."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "`next_cursor` from the previous page."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Legacy; prefer `cursor`."
          },
          {
            "name": "count",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Include `pagination.total`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Payment"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/payments/{id}": {
      "get": {
        "tags": [
          "Payments"
        ],
        "summary": "Get a payment",
        "operationId": "get_v1_payments_id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Payment"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/payments/{id}/void": {
      "post": {
        "tags": [
          "Payments"
        ],
        "summary": "Void a payment",
        "operationId": "post_v1_payments_id_void",
        "description": "Writes a reversal (negative) entry, lowers the order's `amount_paid` and re-opens it if needed. 409 `already_voided` the second time.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Payment"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messages": {
      "post": {
        "tags": [
          "Messages"
        ],
        "summary": "Send a message to a member",
        "operationId": "post_v1_messages",
        "description": "With `text`: sent right away through the organization's line (WhatsApp or SMS) — `status: sent`.\n\nWith `template_id`: queued for the sending engine, which renders the template variables — `status: queued` (any channel, including email).\n\nRespects opt-outs (409 `opted_out`), the organization's kill switch (409 `comms_disabled`) and its send window (409 `outside_send_window`, text only; queued messages wait for the window).\n\nOrganizations in simulation mode record the message but nothing leaves (`status: simulated`).",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "member_id"
                ],
                "properties": {
                  "member_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "channel": {
                    "type": "string",
                    "enum": [
                      "whatsapp",
                      "sms",
                      "email"
                    ],
                    "default": "whatsapp"
                  },
                  "text": {
                    "type": "string",
                    "maxLength": 4000
                  },
                  "template_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/MessageResult"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/conversations/import": {
      "post": {
        "tags": [
          "Messages"
        ],
        "summary": "Import conversation history (messages that happened outside Makatea)",
        "operationId": "post_v1_conversations_import",
        "description": "Up to 20,000 rows per call. Idempotent by `external_id` (or by member + time + direction + text). Imported messages trigger nothing: no auto-replies, no notices, no webhooks. `dry_run` validates without writing. Members are matched by `member_external_ref`, or by `phone` when exactly one member has it.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "dry_run": {
                    "type": "boolean"
                  },
                  "rows": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "occurred_at",
                        "direction",
                        "channel",
                        "body"
                      ],
                      "properties": {
                        "member_external_ref": {
                          "type": "string"
                        },
                        "phone": {
                          "type": "string"
                        },
                        "occurred_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "direction": {
                          "type": "string",
                          "enum": [
                            "inbound",
                            "outbound"
                          ]
                        },
                        "channel": {
                          "type": "string",
                          "enum": [
                            "whatsapp",
                            "email",
                            "sms"
                          ]
                        },
                        "body": {
                          "type": "string"
                        },
                        "subject": {
                          "type": "string"
                        },
                        "external_id": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ConversationImportResult"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhook endpoints",
        "operationId": "get_v1_webhook_endpoints",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Register a webhook endpoint (the signing secret is returned only here)",
        "operationId": "post_v1_webhook_endpoints",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "https only; private/internal hosts are rejected."
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "*",
                        "member.created",
                        "member.updated",
                        "billing_order.created",
                        "billing_order.updated",
                        "billing_order.paid",
                        "billing_order.cancelled",
                        "payment.received",
                        "payment.voided",
                        "promise.created",
                        "promise.kept",
                        "promise.broken",
                        "message.sent",
                        "message.received"
                      ]
                    }
                  },
                  "description": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookEndpointCreated"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-endpoints/{id}": {
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook endpoint (its pending deliveries are dropped)",
        "operationId": "delete_v1_webhook_endpoints_id",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Deleted"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-events": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List emitted events (newest first)",
        "operationId": "get_v1_webhook_events",
        "description": "Events are recorded only while the organization has at least one active endpoint subscribed to that type.",
        "parameters": [
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "created_since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only records created at or after this instant (ISO-8601). Ordering becomes `created_at` ascending."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "`next_cursor` from the previous page."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Legacy; prefer `cursor`."
          },
          {
            "name": "count",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Include `pagination.total`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEvent"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List delivery attempts (newest first)",
        "operationId": "get_v1_webhook_deliveries",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "delivered",
                "failed"
              ]
            }
          },
          {
            "name": "event_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "endpoint_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "`next_cursor` from the previous page."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Legacy; prefer `cursor`."
          },
          {
            "name": "count",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Include `pagination.total`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-deliveries/{id}/retry": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Retry a delivery now (pending or failed)",
        "operationId": "post_v1_webhook_deliveries_id_retry",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/WebhookDelivery"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sequence-runs": {
      "get": {
        "tags": [
          "Operations"
        ],
        "summary": "List scheduled/sent collection messages (newest scheduled first)",
        "operationId": "get_v1_sequence_runs",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "member_id",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "`next_cursor` from the previous page."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Legacy; prefer `cursor`."
          },
          {
            "name": "count",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Include `pagination.total`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SequenceRun"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/flows": {
      "get": {
        "tags": [
          "Operations"
        ],
        "summary": "List the organization's flows",
        "operationId": "get_v1_flows",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Flow"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/templates": {
      "get": {
        "tags": [
          "Operations"
        ],
        "summary": "List the organization's active templates",
        "operationId": "get_v1_templates",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Template"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/stats": {
      "get": {
        "tags": [
          "Operations"
        ],
        "summary": "Dashboard numbers — the same figures the app's Panel shows",
        "operationId": "get_v1_stats",
        "description": "Every money figure comes from the same database function the app uses (`org_metricas_cobranza`), so the API and the Panel never disagree. `metrics` has the full set for the period (`from`/`to`, default: 1st of the current month to today, in the org's timezone): `collected` = money that came in by payment date (voided payments, credit-balance applications and future-dated rows excluded), `overdue`/`upcoming` = real balance (amount − paid − forgiven) of open installments due before / on-or-after today, `aging` by days late (1-9, 10-30, 31-60, 60+), `collection_rate` = of what came due in the period up to today, the share already paid. `metrics.capital_profit` splits the same period into principal and profit: `placed` (principal lent, by credit `start_date`), `collected.capital|interest|late_fee|other|unassigned|no_breakdown|to_credit_balance` (they add up exactly to `collected.total`, the same «collected» as above), `profit` = interest + late fee collected with its `margin` over what was collected with a breakdown, `outstanding` principal and interest (current / overdue) of open installments, and `forgiven` split into principal (a loss), interest, late fee and other. `coverage.status` is `none` when no installment has a breakdown: then every figure is 0 and `no_breakdown` carries the money. `portfolio` and `members` keep their old shape.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Period start (YYYY-MM-DD). Default: 1st of the current month."
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Period end (YYYY-MM-DD). Default: today (org timezone)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Stats"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "`mk_live_…` key."
      },
      "Bearer": {
        "type": "http",
        "scheme": "bearer",
        "description": "`mk_live_…` key, or a session JWT from the Makatea app."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "example": "validation_error"
              },
              "message": {
                "type": "string"
              },
              "request_id": {
                "type": "string"
              },
              "details": {
                "type": "object"
              }
            },
            "required": [
              "code",
              "message",
              "request_id"
            ]
          }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer"
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          },
          "offset": {
            "type": "integer",
            "description": "Only when paging with offset."
          },
          "total": {
            "type": "integer",
            "description": "Only with count=true."
          }
        }
      },
      "Status": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "degraded"
            ]
          },
          "database": {
            "type": "string"
          },
          "database_latency_ms": {
            "type": "integer"
          },
          "api_version": {
            "type": "string"
          },
          "time": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Member": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "external_ref": {
            "type": "string",
            "nullable": true
          },
          "full_name": {
            "type": "string"
          },
          "phone_e164": {
            "type": "string",
            "nullable": true
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "state": {
            "type": "string"
          },
          "identity": {
            "type": "object"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "payment_reference": {
            "type": "string",
            "nullable": true
          },
          "segment_id": {
            "type": "string",
            "nullable": true
          },
          "promised_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "promise_status": {
            "type": "string"
          },
          "promised_amount": {
            "type": "number",
            "nullable": true
          },
          "promise_registered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "credit_balance": {
            "type": "number"
          },
          "do_not_contact": {
            "type": "boolean",
            "description": "Never message this member (withdraws anything queued)."
          },
          "do_not_contact_reason": {
            "type": "string",
            "nullable": true
          },
          "customer_since": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "When it became your customer; `created_at` is when it entered Makatea."
          },
          "import_batch_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BillingOrder": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "member_id": {
            "type": "string",
            "format": "uuid"
          },
          "external_ref": {
            "type": "string",
            "nullable": true
          },
          "amount": {
            "type": "number"
          },
          "amount_paid": {
            "type": "number"
          },
          "amount_forgiven": {
            "type": "number"
          },
          "late_fee": {
            "type": "number"
          },
          "balance": {
            "type": "number",
            "description": "amount − amount_paid − amount_forgiven (late fees are reported apart)."
          },
          "currency": {
            "type": "string"
          },
          "due_date": {
            "type": "string",
            "format": "date"
          },
          "period_label": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "overdue",
              "paid",
              "cancelled"
            ]
          },
          "paid_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "paid_via": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "forgiven_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "forgiven_reason": {
            "type": "string",
            "nullable": true
          },
          "cancel_reason": {
            "type": "string",
            "nullable": true,
            "description": "Why it was cancelled (`DELETE …?reason=`)."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "credit_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Credit (loan) it belongs to."
          },
          "capital_portion": {
            "type": "number",
            "nullable": true,
            "description": "Principal part of the installment (null = no breakdown)."
          },
          "interest_portion": {
            "type": "number",
            "nullable": true,
            "description": "Interest part of the installment."
          },
          "breakdown": {
            "type": "object",
            "properties": {
              "allocation_rule": {
                "type": "string",
                "enum": [
                  "moratorio_interes_capital",
                  "proporcional",
                  "al_liquidar"
                ]
              },
              "capital": {
                "type": "number"
              },
              "interest": {
                "type": "number"
              },
              "late_fee": {
                "type": "number",
                "nullable": true,
                "description": "Collectible late fee inside `amount` (min(late_fee, amount − capital − interest))."
              },
              "other": {
                "type": "number",
                "nullable": true,
                "description": "Rest of `amount` that is neither principal, interest nor late fee."
              },
              "collected": {
                "type": "object",
                "properties": {
                  "capital": {
                    "type": "number"
                  },
                  "interest": {
                    "type": "number"
                  },
                  "late_fee": {
                    "type": "number"
                  },
                  "other": {
                    "type": "number"
                  },
                  "unassigned": {
                    "type": "number"
                  }
                },
                "description": "Net of voided payments."
              },
              "forgiven": {
                "type": "object",
                "properties": {
                  "capital": {
                    "type": "number"
                  },
                  "interest": {
                    "type": "number"
                  },
                  "late_fee": {
                    "type": "number"
                  },
                  "other": {
                    "type": "number"
                  }
                }
              },
              "outstanding": {
                "type": "object",
                "properties": {
                  "capital": {
                    "type": "number"
                  },
                  "interest": {
                    "type": "number"
                  },
                  "late_fee": {
                    "type": "number"
                  },
                  "other": {
                    "type": "number"
                  }
                },
                "nullable": true,
                "description": "Only for open installments."
              }
            },
            "nullable": true,
            "description": "null when the installment has no breakdown (figures are never invented)."
          }
        }
      },
      "Payment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "billing_order_id": {
            "type": "string",
            "format": "uuid"
          },
          "member_id": {
            "type": "string",
            "format": "uuid"
          },
          "amount": {
            "type": "number"
          },
          "kind": {
            "type": "string",
            "enum": [
              "payment",
              "reversal"
            ]
          },
          "method": {
            "type": "string",
            "nullable": true
          },
          "reference": {
            "type": "string",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "voids_payment_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "voided": {
            "type": "boolean"
          },
          "paid_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the money was received."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "recorded_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was recorded in Makatea."
          },
          "allocation": {
            "type": "object",
            "properties": {
              "capital": {
                "type": "number"
              },
              "interest": {
                "type": "number"
              },
              "late_fee": {
                "type": "number"
              },
              "other": {
                "type": "number"
              },
              "unassigned": {
                "type": "number"
              }
            },
            "nullable": true,
            "description": "How this entry was applied to its installment. Adds up to `amount`. `unassigned` = beyond the installment's parts or, with rule `al_liquidar`, a partial payment not yet recognized (the settling payment then carries it negative). null when the installment has no breakdown."
          }
        }
      },
      "PaymentResult": {
        "type": "object",
        "properties": {
          "order_id": {
            "type": "string",
            "format": "uuid"
          },
          "amount_paid": {
            "type": "number"
          },
          "status": {
            "type": "string"
          },
          "fully_paid": {
            "type": "boolean"
          },
          "excess_applied_to_other_orders": {
            "type": "number"
          },
          "credit_added": {
            "type": "number"
          },
          "already_applied": {
            "type": "boolean"
          },
          "payment": {
            "$ref": "#/components/schemas/Payment"
          },
          "order": {
            "$ref": "#/components/schemas/BillingOrder"
          }
        }
      },
      "PromiseState": {
        "type": "object",
        "properties": {
          "member_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "none",
              "active",
              "kept",
              "broken",
              "cancelled"
            ]
          },
          "current": {
            "type": "object",
            "properties": {
              "promised_date": {
                "type": "string",
                "format": "date"
              },
              "amount": {
                "type": "number",
                "nullable": true
              },
              "status": {
                "type": "string"
              },
              "registered_at": {
                "type": "string",
                "format": "date-time"
              }
            },
            "nullable": true
          },
          "history": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "Credit": {
        "type": "object",
        "properties": {
          "member_id": {
            "type": "string",
            "format": "uuid"
          },
          "credit_balance": {
            "type": "number"
          },
          "ledger": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "amount": {
                  "type": "number"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "excedente",
                    "aplicado",
                    "ajuste"
                  ]
                },
                "applied_order_id": {
                  "type": "string",
                  "format": "uuid",
                  "nullable": true
                },
                "source_order_id": {
                  "type": "string",
                  "format": "uuid",
                  "nullable": true
                },
                "reference": {
                  "type": "string",
                  "nullable": true
                },
                "notes": {
                  "type": "string",
                  "nullable": true
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "CreditApplied": {
        "type": "object",
        "properties": {
          "member_id": {
            "type": "string",
            "format": "uuid"
          },
          "applied": {
            "type": "number"
          },
          "credit_balance": {
            "type": "number"
          }
        }
      },
      "Interaction": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "channel": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "text": {
            "type": "string",
            "nullable": true
          },
          "attachment": {
            "type": "string",
            "nullable": true
          },
          "classification": {
            "type": "string",
            "nullable": true
          },
          "sequence_run_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "simulated": {
            "type": "boolean"
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MessageResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "enum": [
              "interaction",
              "sequence_run"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "sent",
              "simulated",
              "queued",
              "queued_simulated"
            ]
          },
          "channel": {
            "type": "string"
          },
          "member_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "ConversationImportResult": {
        "type": "object",
        "properties": {
          "inserted": {
            "type": "integer"
          },
          "skipped_duplicates": {
            "type": "integer"
          },
          "unmatched": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "row": {
                  "type": "integer"
                },
                "reason": {
                  "type": "string"
                }
              }
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "row": {
                  "type": "integer"
                },
                "error": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Document": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "member_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "mime_type": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          },
          "external_ref": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "storage_path": {
            "type": "string"
          },
          "download_url": {
            "type": "string",
            "nullable": true,
            "description": "Signed URL, valid 1 hour."
          },
          "download_url_expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Deleted": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "deleted": {
            "type": "boolean"
          }
        }
      },
      "PaymentLink": {
        "type": "object"
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "active": {
            "type": "boolean"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WebhookEndpointCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEndpoint"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "example": "whsec_…",
                "description": "Shown only in this response."
              }
            }
          }
        ]
      },
      "WebhookEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "type": {
            "type": "string",
            "enum": [
              "member.created",
              "member.updated",
              "billing_order.created",
              "billing_order.updated",
              "billing_order.paid",
              "billing_order.cancelled",
              "payment.received",
              "payment.voided",
              "promise.created",
              "promise.kept",
              "promise.broken",
              "message.sent",
              "message.received"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "object"
          }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "event_id": {
            "type": "string",
            "format": "uuid"
          },
          "event_type": {
            "type": "string"
          },
          "endpoint_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivered",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "next_attempt_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_status": {
            "type": "integer",
            "nullable": true
          },
          "last_error": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "delivered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "SequenceRun": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "member_id": {
            "type": "string",
            "format": "uuid"
          },
          "billing_order_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "template_id": {
            "type": "string",
            "format": "uuid"
          },
          "state": {
            "type": "string"
          },
          "channel": {
            "type": "string"
          },
          "scheduled_at": {
            "type": "string",
            "format": "date-time"
          },
          "outcome": {
            "type": "string",
            "nullable": true
          },
          "attempts": {
            "type": "integer"
          }
        }
      },
      "Flow": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "steps": {
            "type": "integer"
          }
        }
      },
      "Template": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "channel": {
            "type": "string"
          },
          "trigger_type": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "subject": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "Stats": {
        "type": "object"
      },
      "Loan": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "member_id": {
            "type": "string",
            "format": "uuid"
          },
          "member_name": {
            "type": "string",
            "nullable": true
          },
          "external_ref": {
            "type": "string",
            "nullable": true,
            "description": "Your folio."
          },
          "principal": {
            "type": "number",
            "description": "Money lent."
          },
          "total_amount": {
            "type": "number",
            "nullable": true
          },
          "periods": {
            "type": "integer",
            "nullable": true
          },
          "frequency": {
            "type": "string",
            "nullable": true
          },
          "start_date": {
            "type": "string",
            "format": "date",
            "description": "Date the money was lent."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "liquidated",
              "cancelled"
            ]
          },
          "notes": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "totals": {
            "type": "object",
            "properties": {
              "orders": {
                "type": "integer"
              },
              "open_orders": {
                "type": "integer"
              },
              "orders_with_breakdown": {
                "type": "integer"
              },
              "scheduled_capital": {
                "type": "number",
                "nullable": true
              },
              "scheduled_interest": {
                "type": "number",
                "nullable": true
              },
              "capital_recovered": {
                "type": "number"
              },
              "interest_collected": {
                "type": "number"
              },
              "late_fee_collected": {
                "type": "number"
              },
              "capital_forgiven": {
                "type": "number"
              },
              "interest_forgiven": {
                "type": "number"
              },
              "capital_outstanding": {
                "type": "number"
              },
              "interest_outstanding": {
                "type": "number"
              },
              "balance": {
                "type": "number"
              },
              "paid_without_ledger": {
                "type": "number",
                "description": "Paid amounts imported as history, with no ledger entry."
              },
              "last_payment_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          }
        }
      },
      "LoanDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Loan"
          },
          {
            "type": "object",
            "properties": {
              "billing_orders": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/BillingOrder"
                }
              }
            }
          }
        ]
      },
      "AllocationRule": {
        "type": "object",
        "properties": {
          "rule": {
            "type": "string"
          },
          "rules": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "orders_reapplied": {
            "type": "integer"
          }
        }
      }
    }
  }
}
