{
 "openapi": "3.1.0",
 "info": {
  "title": "Fintable API",
  "version": "2.0.0",
  "description": "First-party API for your Fintable account: bank connections, accounts, transactions, holdings, the categorizer, sync, and integrations. Full docs: https://fintable.io/docs"
 },
 "servers": [
  {
   "url": "https://fintable.io"
  }
 ],
 "security": [
  {
   "bearerAuth": []
  }
 ],
 "components": {
  "securitySchemes": {
   "bearerAuth": {
    "type": "http",
    "scheme": "bearer",
    "description": "Personal Access Token or OAuth access token."
   },
   "oauth2": {
    "type": "oauth2",
    "flows": {
     "authorizationCode": {
      "authorizationUrl": "https://fintable.io/oauth/authorize",
      "tokenUrl": "https://fintable.io/oauth/token",
      "scopes": {
       "read": "Read all data on the account",
       "write": "Modify data on the account",
       "mcp:use": "Full read and write access via the MCP server"
      }
     }
    }
   }
  },
  "schemas": {
   "Error": {
    "type": "object",
    "properties": {
     "error": {
      "type": "object",
      "properties": {
       "type": {
        "type": "string"
       },
       "message": {
        "type": "string"
       },
       "errors": {
        "type": "object",
        "additionalProperties": {
         "type": "array",
         "items": {
          "type": "string"
         }
        }
       }
      },
      "required": [
       "type",
       "message"
      ]
     }
    }
   },
   "DataObject": {
    "type": "object",
    "properties": {
     "data": {
      "type": "object"
     }
    }
   },
   "DataList": {
    "type": "object",
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "type": "object"
      }
     }
    }
   },
   "TransactionsPage": {
    "type": "object",
    "properties": {
     "data": {
      "type": "array",
      "items": {
       "type": "object"
      }
     },
     "next_cursor": {
      "type": [
       "string",
       "null"
      ]
     }
    }
   }
  },
  "responses": {
   "Object": {
    "description": "A {\"data\": {...}} envelope.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/DataObject"
      }
     }
    }
   },
   "List": {
    "description": "A {\"data\": [...]} envelope.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/DataList"
      }
     }
    }
   },
   "Error": {
    "description": "The standard error envelope.",
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   }
  },
  "parameters": {
   "id": {
    "name": "id",
    "in": "path",
    "required": true,
    "schema": {
     "type": "string"
    }
   }
  }
 },
 "paths": {
  "/api/v2/openapi.json": {
   "get": {
    "summary": "This OpenAPI document.",
    "security": [],
    "responses": {
     "200": {
      "description": "OpenAPI 3.1 JSON."
     }
    }
   }
  },
  "/api/v2/guide": {
   "get": {
    "summary": "The Fintable user guide as markdown (public).",
    "security": [],
    "parameters": [
     {
      "name": "locale",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "main",
        "uk",
        "es"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "text/markdown"
     }
    }
   }
  },
  "/api/v2/docs": {
   "get": {
    "summary": "The API documentation as markdown (public).",
    "security": [],
    "responses": {
     "200": {
      "description": "text/markdown"
     }
    }
   }
  },
  "/api/v2/institutions": {
   "get": {
    "summary": "Search the public institutions directory (10 results/page, meta.has_more).",
    "security": [],
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "schema": {
       "type": "string",
       "minLength": 3
      }
     },
     {
      "name": "domain",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "country",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "provider",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "PLAID",
        "NORDIGEN",
        "AKOYA",
        "FINICITY",
        "MERCURY",
        "SNAPTRADE"
       ]
      }
     },
     {
      "name": "page",
      "in": "query",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "$ref": "#/components/responses/List"
     }
    }
   }
  },
  "/api/v2/me": {
   "get": {
    "summary": "Profile + billing metadata (name, tier, limits, renewal).",
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     }
    }
   }
  },
  "/api/v2/connections": {
   "get": {
    "summary": "All bank connections with health and live sync status.",
    "responses": {
     "200": {
      "$ref": "#/components/responses/List"
     }
    }
   }
  },
  "/api/v2/connections/{id}": {
   "get": {
    "summary": "One connection.",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     },
     "404": {
      "$ref": "#/components/responses/Error"
     }
    }
   },
   "patch": {
    "summary": "Rename a connection and/or set its sync start date (enabled accounts only).",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "name": {
          "type": [
           "string",
           "null"
          ],
          "maxLength": 64
         },
         "sync_start_date": {
          "type": [
           "string",
           "null"
          ],
          "format": "date"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     }
    }
   },
   "delete": {
    "summary": "Disconnect the bank and purge its data (async).",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "responses": {
     "202": {
      "$ref": "#/components/responses/Object"
     }
    }
   }
  },
  "/api/v2/connections/link": {
   "post": {
    "summary": "Mint a single-use signed browser URL to connect a new bank (30-min TTL).",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "institution": {
          "type": [
           "string",
           "null"
          ],
          "description": "Institution slug from GET /institutions."
         }
        }
       }
      }
     }
    },
    "responses": {
     "201": {
      "$ref": "#/components/responses/Object"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/api/v2/connections/{id}/link": {
   "post": {
    "summary": "Mint a single-use reconnect URL for an existing connection.",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "responses": {
     "201": {
      "$ref": "#/components/responses/Object"
     }
    }
   }
  },
  "/api/v2/accounts": {
   "get": {
    "summary": "All accounts, including disabled ones.",
    "parameters": [
     {
      "name": "connection_id",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "ids[]",
      "in": "query",
      "schema": {
       "type": "array",
       "items": {
        "type": "string"
       }
      }
     },
     {
      "name": "enabled",
      "in": "query",
      "schema": {
       "type": "boolean"
      }
     }
    ],
    "responses": {
     "200": {
      "$ref": "#/components/responses/List"
     }
    }
   }
  },
  "/api/v2/accounts/{id}": {
   "get": {
    "summary": "One account.",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     },
     "404": {
      "$ref": "#/components/responses/Error"
     }
    }
   },
   "patch": {
    "summary": "Update an account. WARNING: enabled=false permanently deletes the account's transactions.",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "display_name": {
          "type": [
           "string",
           "null"
          ],
          "maxLength": 64
         },
         "sync_start_date": {
          "type": [
           "string",
           "null"
          ],
          "format": "date"
         },
         "enabled": {
          "type": "boolean"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/api/v2/accounts/{id}/holdings": {
   "get": {
    "summary": "Investment holdings — one daily snapshot (default: latest).",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     },
     {
      "name": "date",
      "in": "query",
      "schema": {
       "type": "string",
       "format": "date"
      }
     }
    ],
    "responses": {
     "200": {
      "$ref": "#/components/responses/List"
     }
    }
   }
  },
  "/api/v2/accounts/{id}/transactions": {
   "get": {
    "summary": "Transactions of one account (same filters and cursor pagination as /transactions).",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "responses": {
     "200": {
      "description": "Cursor-paged transactions.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/TransactionsPage"
        }
       }
      }
     }
    }
   }
  },
  "/api/v2/transactions": {
   "get": {
    "summary": "List transactions (filters + opaque cursor pagination).",
    "parameters": [
     {
      "name": "date_from",
      "in": "query",
      "schema": {
       "type": "string",
       "format": "date"
      }
     },
     {
      "name": "date_to",
      "in": "query",
      "schema": {
       "type": "string",
       "format": "date"
      }
     },
     {
      "name": "account_ids[]",
      "in": "query",
      "schema": {
       "type": "array",
       "items": {
        "type": "string"
       }
      }
     },
     {
      "name": "category_ids[]",
      "in": "query",
      "schema": {
       "type": "array",
       "items": {
        "type": "string"
       }
      }
     },
     {
      "name": "pending",
      "in": "query",
      "schema": {
       "type": "boolean"
      }
     },
     {
      "name": "amount_min",
      "in": "query",
      "schema": {
       "type": "number"
      }
     },
     {
      "name": "amount_max",
      "in": "query",
      "schema": {
       "type": "number"
      }
     },
     {
      "name": "q",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "description",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "updated_since",
      "in": "query",
      "schema": {
       "type": "string",
       "format": "date-time"
      }
     },
     {
      "name": "order",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "date",
        "updated"
       ]
      }
     },
     {
      "name": "limit",
      "in": "query",
      "schema": {
       "type": "integer",
       "maximum": 500
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "include",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "raw"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Cursor-paged transactions.",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/TransactionsPage"
        }
       }
      }
     },
     "400": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/api/v2/transactions/bulk": {
   "patch": {
    "summary": "Apply one category to many transactions (ids XOR filters; max 10,000; dry_run supported).",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "category_id"
        ],
        "properties": {
         "category_id": {
          "type": [
           "string",
           "null"
          ]
         },
         "ids": {
          "type": "array",
          "items": {
           "type": "string"
          },
          "maxItems": 10000
         },
         "filters": {
          "type": "object"
         },
         "dry_run": {
          "type": "boolean"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/api/v2/transactions/{id}": {
   "get": {
    "summary": "One transaction.",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     },
     {
      "name": "include",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "raw"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     },
     "404": {
      "$ref": "#/components/responses/Error"
     }
    }
   },
   "patch": {
    "summary": "Set or clear the transaction's category (null uncategorizes and clears the manual override).",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "category_id"
        ],
        "properties": {
         "category_id": {
          "type": [
           "string",
           "null"
          ]
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/api/v2/sync": {
   "get": {
    "summary": "Sync schedule (default sweep window + custom schedules), active syncs, per-connection status.",
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     }
    }
   },
   "post": {
    "summary": "Sync all connections (per-connection outcomes: started | already_syncing). Requires an active subscription or trial.",
    "responses": {
     "200": {
      "$ref": "#/components/responses/List"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/api/v2/sync/{connection_id}": {
   "post": {
    "summary": "Sync one connection.",
    "parameters": [
     {
      "name": "connection_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "$ref": "#/components/responses/List"
     },
     "403": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/api/v2/integrations": {
   "get": {
    "summary": "Airtable + Google Sheets integration status and health.",
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     }
    }
   }
  },
  "/api/v2/categorizer/categories": {
   "get": {
    "summary": "All categories.",
    "responses": {
     "200": {
      "$ref": "#/components/responses/List"
     }
    }
   },
   "post": {
    "summary": "Create a category.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "name",
         "header"
        ],
        "properties": {
         "name": {
          "type": "string"
         },
         "header": {
          "type": "string"
         },
         "color": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "201": {
      "$ref": "#/components/responses/Object"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/api/v2/categorizer/categories/{id}": {
   "get": {
    "summary": "One category.",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     }
    }
   },
   "patch": {
    "summary": "Update a category (renames propagate to spreadsheets).",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "name": {
          "type": "string"
         },
         "header": {
          "type": "string"
         },
         "color": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     }
    }
   },
   "delete": {
    "summary": "Delete a category (409 while rules reference it; transactions become uncategorized).",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     },
     "409": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/api/v2/categorizer/rules": {
   "get": {
    "summary": "All rules (metadata; fetch one rule for its JSONLogic).",
    "responses": {
     "200": {
      "$ref": "#/components/responses/List"
     }
    }
   },
   "post": {
    "summary": "Create a simple or advanced rule; queues an ordered all-rules pass (202).",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "type"
        ],
        "properties": {
         "type": {
          "type": "string",
          "enum": [
           "simple",
           "advanced"
          ]
         },
         "text": {
          "type": "string",
          "minLength": 3,
          "maxLength": 128
         },
         "category_id": {
          "type": "string"
         },
         "name": {
          "type": "string"
         },
         "logic": {
          "type": "string",
          "description": "JSONLogic as a JSON string; top-level if; literal category-id result branches."
         }
        }
       }
      }
     }
    },
    "responses": {
     "202": {
      "$ref": "#/components/responses/Object"
     },
     "422": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/api/v2/categorizer/rules/{id}": {
   "get": {
    "summary": "One rule with full JSONLogic.",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     }
    }
   },
   "patch": {
    "summary": "Update a rule (priority/logic changes queue an all-rules pass, 202).",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "name": {
          "type": "string"
         },
         "priority": {
          "type": "integer"
         },
         "logic": {
          "type": "string"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     },
     "202": {
      "$ref": "#/components/responses/Object"
     }
    }
   },
   "delete": {
    "summary": "Delete a rule (previously categorized transactions keep their categories).",
    "parameters": [
     {
      "$ref": "#/components/parameters/id"
     }
    ],
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     }
    }
   }
  },
  "/api/v2/categorizer/sync": {
   "post": {
    "summary": "Queue a full rules pass + spreadsheet export (202).",
    "responses": {
     "202": {
      "$ref": "#/components/responses/Object"
     }
    }
   }
  },
  "/api/v2/categorizer/status": {
   "get": {
    "summary": "Categorizer run state: versions, settled flag, per-destination export state, failures.",
    "responses": {
     "200": {
      "$ref": "#/components/responses/Object"
     }
    }
   }
  }
 }
}
