{"openapi":"3.0.0","info":{"title":"Purple Retail Banking API","version":"1.0.0","description":"Core retail banking source system: customers, accounts, transactions, cards, products, loans & applications, plus the relationship/servicing layer (interactions, opportunities, complaints). Every record carries a `country` (IN | AU | US) and self-describing money; narrow any list with the `country` filter.\n\n---\n\n**Acting as a user (`X-PS-Impersonate-User`).** Every endpoint accepts an optional `X-PS-Impersonate-User` request header whose value is the email of the instance user to act as. The effective user resolves in this order: the header if present, else the instance's configured default MCP user, else none. User-scoped paths additionally accept the literal `me` in place of an email, and Graph-style apps also serve `/me/…` twins of their `/users/{email}/…` paths that resolve the same way. This header is honored by all operations but is intentionally omitted from each operation's parameter list so it is not surfaced as a per-tool argument by MCP clients; see the `ImpersonateUser` entry under `components.parameters`."},"servers":[{"url":"https://marketplace.moveworks.com/api/purple-suite/retail-banking"}],"security":[{"patAuth":[]}],"components":{"securitySchemes":{"patAuth":{"type":"http","scheme":"bearer"}},"parameters":{"InstanceId":{"in":"header","name":"X-Instance-ID","required":true,"schema":{"type":"string"}},"ImpersonateUser":{"in":"header","name":"X-PS-Impersonate-User","required":false,"schema":{"type":"string","format":"email"},"description":"Optional. Act as a specific user in the target instance — the value is that user's email (must exist in the instance). Resolution order for the effective user: (1) this header, if set; (2) the instance's configured default MCP user; (3) otherwise no user is bound. User-scoped paths (e.g. /users/{email}/…) also accept the literal \"me\", and Graph-style apps serve /me/… twins of those paths (Graph: /me ≡ /users/{signed-in-user}) — both resolve to whatever this header (or the default MCP user) points at. An explicit path email always wins over the header. The unified MCP server does not send this header — it relies on the per-instance default — so it is primarily for direct REST/HTTP callers."}},"schemas":{"Customer":{"type":"object","properties":{"id":{"type":"string","readOnly":true,"example":"CUST-0001"},"country":{"type":"string","enum":["IN","AU","US","GB","KR","DE"],"example":"IN"},"firstName":{"type":"string","example":"Ananya"},"lastName":{"type":"string","example":"Sharma"},"email":{"type":"string","format":"email"},"phone":{"type":"string","example":"+91 98765 43210"},"address":{"type":"string"},"city":{"type":"string","example":"Mumbai"},"segment":{"type":"string","enum":["mass","affluent","hni","nri","sme"],"example":"affluent"},"kyc_status":{"type":"string","enum":["verified","pending","expired","in_remediation"],"example":"verified"},"risk_rating":{"type":"string","enum":["low","medium","high"],"example":"low"},"relationship_manager":{"type":"string","format":"email"},"home_branch_id":{"type":"string","example":"BR-IN-014"},"tax_id":{"type":"object","properties":{"type":{"type":"string","example":"PAN"},"masked":{"type":"string","example":"ABCDE****F"}}},"credit_score":{"type":"object","properties":{"system":{"type":"string","example":"CIBIL"},"value":{"type":"integer","example":782}}},"onboarded_at":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time","readOnly":true},"updatedAt":{"type":"string","format":"date-time","readOnly":true}}},"Account":{"type":"object","properties":{"id":{"type":"string","readOnly":true,"example":"ACCT-0001"},"customer_id":{"type":"string","example":"CUST-0001"},"country":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"type":{"type":"string","example":"Savings"},"currency":{"type":"string","example":"INR"},"balance":{"type":"object","properties":{"amount":{"type":"number","example":250000},"currency":{"type":"string","example":"INR"},"amount_formatted":{"type":"string","example":"₹2,50,000"}}},"status":{"type":"string","enum":["active","dormant","frozen","closed"],"example":"active"},"freeze_reason":{"type":"string","nullable":true},"branch_id":{"type":"string"},"routing":{"type":"object","properties":{"label":{"type":"string","example":"IFSC"},"code":{"type":"string","example":"HDFC0001234"},"accountNumber":{"type":"string"}}},"status_history":{"type":"array","readOnly":true,"items":{"type":"object","properties":{"status":{"type":"string"},"reason":{"type":"string","nullable":true},"recorded_at":{"type":"string","format":"date-time"}}}},"opened_at":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time","readOnly":true},"updatedAt":{"type":"string","format":"date-time","readOnly":true}}},"Transaction":{"type":"object","properties":{"id":{"type":"string","readOnly":true,"example":"TXN-00001"},"account_id":{"type":"string","example":"ACCT-0001"},"customer_id":{"type":"string","example":"CUST-0001"},"country":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"amount":{"type":"object","properties":{"amount":{"type":"number","example":250000},"currency":{"type":"string","example":"INR"},"amount_formatted":{"type":"string","example":"₹2,50,000"}}},"direction":{"type":"string","enum":["debit","credit"],"example":"debit"},"channel":{"type":"string","example":"UPI"},"counterparty":{"type":"string","example":"Amazon Pay"},"status":{"type":"string","enum":["posted","pending","disputed","reversed"],"example":"posted"},"is_flagged":{"type":"boolean","example":false},"posted_at":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time","readOnly":true},"updatedAt":{"type":"string","format":"date-time","readOnly":true}}},"Card":{"type":"object","properties":{"id":{"type":"string","readOnly":true,"example":"CARD-0001"},"customer_id":{"type":"string"},"account_id":{"type":"string"},"country":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"network":{"type":"string","enum":["visa","mastercard","rupay","amex"],"example":"rupay"},"type":{"type":"string","enum":["debit","credit"],"example":"debit"},"status":{"type":"string","enum":["active","blocked","expired"],"example":"active"},"last_four":{"type":"string","example":"4321"},"block_reason":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time","readOnly":true},"updatedAt":{"type":"string","format":"date-time","readOnly":true}}},"Product":{"type":"object","properties":{"id":{"type":"string","readOnly":true,"example":"PROD-001"},"country":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"name":{"type":"string","example":"Tax-Saver Fixed Deposit"},"category":{"type":"string","enum":["deposit","lending","card","investment","insurance"],"example":"deposit"},"interest_rate":{"type":"number","example":7.1},"min_amount":{"type":"object","properties":{"amount":{"type":"number","example":250000},"currency":{"type":"string","example":"INR"},"amount_formatted":{"type":"string","example":"₹2,50,000"}}},"eligibility_summary":{"type":"string"},"active":{"type":"boolean","example":true},"createdAt":{"type":"string","format":"date-time","readOnly":true},"updatedAt":{"type":"string","format":"date-time","readOnly":true}}},"Loan":{"type":"object","properties":{"id":{"type":"string","readOnly":true,"example":"LOAN-0001"},"customer_id":{"type":"string","example":"CUST-0001"},"product_id":{"type":"string"},"country":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"type":{"type":"string","example":"home"},"principal":{"type":"object","properties":{"amount":{"type":"number","example":250000},"currency":{"type":"string","example":"INR"},"amount_formatted":{"type":"string","example":"₹2,50,000"}}},"outstanding":{"type":"object","properties":{"amount":{"type":"number","example":250000},"currency":{"type":"string","example":"INR"},"amount_formatted":{"type":"string","example":"₹2,50,000"}}},"interest_rate":{"type":"number","example":8.6},"term_months":{"type":"integer","example":240},"status":{"type":"string","enum":["active","delinquent","closed","written_off"],"example":"active"},"dpd":{"type":"integer","description":"Days past due","example":0},"delinquency_bucket":{"type":"string","enum":["current","1-30","31-60","61-90","90+"],"example":"current"},"next_due_date":{"type":"string","format":"date"},"repayment_schedule":{"type":"array","items":{"type":"object","properties":{"installment_no":{"type":"integer"},"due_date":{"type":"string","format":"date"},"amount":{"type":"object","properties":{"amount":{"type":"number","example":250000},"currency":{"type":"string","example":"INR"},"amount_formatted":{"type":"string","example":"₹2,50,000"}}},"status":{"type":"string","enum":["paid","due","overdue"]},"paid_at":{"type":"string","format":"date-time","nullable":true}}}},"createdAt":{"type":"string","format":"date-time","readOnly":true},"updatedAt":{"type":"string","format":"date-time","readOnly":true}}},"LoanApplication":{"type":"object","properties":{"id":{"type":"string","readOnly":true,"example":"LAPP-0001"},"customer_id":{"type":"string","example":"CUST-0001"},"product_id":{"type":"string"},"country":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"type":{"type":"string","example":"home"},"requested_amount":{"type":"object","properties":{"amount":{"type":"number","example":250000},"currency":{"type":"string","example":"INR"},"amount_formatted":{"type":"string","example":"₹2,50,000"}}},"purpose":{"type":"string"},"stage":{"type":"string","enum":["submitted","docs_pending","eligibility","underwriting","approved","rejected","disbursed"],"example":"docs_pending"},"required_documents":{"type":"array","items":{"type":"object","properties":{"doc_type":{"type":"string"},"status":{"type":"string","enum":["missing","received","verified"]}}}},"eligibility_score":{"type":"integer","example":71},"decision_reason":{"type":"string","nullable":true},"assigned_rm":{"type":"string","format":"email"},"stage_history":{"type":"array","readOnly":true,"items":{"type":"object","properties":{"stage":{"type":"string"},"reason":{"type":"string","nullable":true},"recorded_at":{"type":"string","format":"date-time"}}}},"createdAt":{"type":"string","format":"date-time","readOnly":true},"updatedAt":{"type":"string","format":"date-time","readOnly":true}}},"Interaction":{"type":"object","properties":{"id":{"type":"string","readOnly":true,"example":"INT-0001"},"customer_id":{"type":"string","example":"CUST-0001"},"country":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"type":{"type":"string","enum":["call","meeting","note","visit"],"example":"meeting"},"subject":{"type":"string","example":"FD maturity review"},"summary":{"type":"string"},"rm":{"type":"string","format":"email"},"occurred_at":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time","readOnly":true},"updatedAt":{"type":"string","format":"date-time","readOnly":true}}},"Opportunity":{"type":"object","properties":{"id":{"type":"string","readOnly":true,"example":"OPP-0001"},"customer_id":{"type":"string","example":"CUST-0001"},"country":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"name":{"type":"string","example":"FD renewal → Tax-saver FD upsell"},"product_category":{"type":"string","enum":["deposit","lending","card","investment","insurance"],"example":"deposit"},"stage":{"type":"string","enum":["identified","proposed","accepted","declined"],"example":"identified"},"recommendation_reason":{"type":"string","example":"FD maturing in 12 days; eligible for higher tax-saver rate"},"value":{"type":"object","properties":{"amount":{"type":"number","example":250000},"currency":{"type":"string","example":"INR"},"amount_formatted":{"type":"string","example":"₹2,50,000"}}},"createdAt":{"type":"string","format":"date-time","readOnly":true},"updatedAt":{"type":"string","format":"date-time","readOnly":true}}},"Complaint":{"type":"object","properties":{"id":{"type":"string","readOnly":true,"example":"CMP-0001"},"customer_id":{"type":"string","example":"CUST-0001"},"account_id":{"type":"string","nullable":true},"country":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"category":{"type":"string","enum":["payments","cards","loans","accounts","atm","digital"],"example":"payments"},"channel":{"type":"string","enum":["branch","call_centre","mobile_app","email"],"example":"mobile_app"},"title":{"type":"string","example":"Unauthorised UPI transaction"},"status":{"type":"string","enum":["logged","investigating","resolved","closed"],"example":"logged"},"sla_policy_id":{"type":"string","nullable":true},"sla_due":{"type":"string","format":"date-time","nullable":true},"known_issue_id":{"type":"string","nullable":true},"resolution_notes":{"type":"string","nullable":true},"status_history":{"type":"array","readOnly":true,"items":{"type":"object","properties":{"status":{"type":"string"},"reason":{"type":"string","nullable":true},"recorded_at":{"type":"string","format":"date-time"}}}},"createdAt":{"type":"string","format":"date-time","readOnly":true},"updatedAt":{"type":"string","format":"date-time","readOnly":true}}},"KnownIssue":{"type":"object","properties":{"id":{"type":"string","readOnly":true,"example":"KI-001"},"country":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"title":{"type":"string","example":"UPI outage – NPCI"},"affected_service":{"type":"string","example":"UPI"},"status":{"type":"string","enum":["known_error","resolved"],"example":"known_error"},"workaround":{"type":"string"},"started_at":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time","readOnly":true},"updatedAt":{"type":"string","format":"date-time","readOnly":true}}},"SlaPolicy":{"type":"object","properties":{"id":{"type":"string","readOnly":true,"example":"SLA-001"},"country":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"name":{"type":"string","example":"UPI Dispute Resolution SLA"},"category":{"type":"string","enum":["payments","cards","loans","accounts","atm","digital"],"example":"payments"},"response_hours":{"type":"number","example":4},"resolution_hours":{"type":"number","example":48},"regulatory_clock":{"type":"boolean","example":true},"createdAt":{"type":"string","format":"date-time","readOnly":true},"updatedAt":{"type":"string","format":"date-time","readOnly":true}}}}},"paths":{"/customers":{"get":{"summary":"List banking customers","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Max items per page (default 50)"},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Pass the previous response's nextCursor to fetch the next page"},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"country","required":false,"schema":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"description":"Filter to a single country (IN | AU | US)"},{"in":"query","name":"segment","required":false,"schema":{"type":"string","enum":["mass","affluent","hni","nri","sme"]},"description":"Filter by customer segment"},{"in":"query","name":"kyc_status","required":false,"schema":{"type":"string","enum":["verified","pending","expired","in_remediation"]},"description":"Filter by KYC status"},{"in":"query","name":"risk_rating","required":false,"schema":{"type":"string","enum":["low","medium","high"]},"description":"Filter by risk rating"},{"in":"query","name":"relationship_manager","required":false,"schema":{"type":"string"},"description":"Filter by relationship manager email"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Customer"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page"},"total":{"type":"integer","description":"Total documents matching the filter"}}}}}}},"description":"Purple Retail Banking customers are the retail parties (individuals and SMEs) who hold the accounts, cards and loans in this bank, each with a segment, KYC status, risk rating, relationship manager and country (IN | AU | US). Not CRM accounts or contacts (B2B sales) and not internal employees. Narrow results server-side with this endpoint's named filter params (country, customer_id, status, …) or with OData $filter, $orderby, $select, $search, $top, $skip and $count (MCP clients send those as _filter, _orderby, …) instead of paging through everything."},"post":{"summary":"Onboard customer","parameters":[{"$ref":"#/components/parameters/InstanceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}}},"description":"Create a new retail banking customer record. Seldom the right move during a servicing conversation: look the person up with the list tool first, since duplicate customers orphan accounts and cards."}},"/customers/{id}":{"get":{"summary":"Get banking customer","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"404":{"description":"Not found"}},"description":"Fetch one banking customer by id (e.g. CUST-0001) with segment, KYC status, risk rating, credit score, relationship manager and home branch. To get their accounts, cards, loans and open complaints in a single call, use the customer portfolio tool instead."},"patch":{"summary":"Update banking customer","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Fields to update (partial)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Customer"}}}},"404":{"description":"Not found"}},"description":"Partially update a banking customer's profile — contact details, address, segment, relationship manager or risk rating. KYC remediation and watchlist handling live in Purple Risk & Compliance, not here."},"delete":{"summary":"Delete banking customer","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string"}}}}}},"404":{"description":"Not found"}},"description":"Permanently delete a banking customer record. Destructive and irreversible, and it orphans their accounts, cards, loans and complaints — effectively never correct in a servicing flow."}},"/customers/{id}/portfolio":{"get":{"summary":"Get customer 360 portfolio","description":"One-call briefing on a single banking customer: the customer record plus their accounts, cards, loans, opportunities, recent interactions and open complaints. Prefer this over five separate list calls whenever the question is about one named customer.","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Aggregated customer portfolio","content":{"application/json":{"schema":{"type":"object","properties":{"customer":{"$ref":"#/components/schemas/Customer"},"accounts":{"type":"array","items":{"$ref":"#/components/schemas/Account"}},"cards":{"type":"array","items":{"$ref":"#/components/schemas/Card"}},"loans":{"type":"array","items":{"$ref":"#/components/schemas/Loan"}},"opportunities":{"type":"array","items":{"$ref":"#/components/schemas/Opportunity"}},"recent_interactions":{"type":"array","items":{"$ref":"#/components/schemas/Interaction"}},"open_complaints":{"type":"array","items":{"$ref":"#/components/schemas/Complaint"}}}}}}},"404":{"description":"Customer not found"}}}},"/bank_accounts":{"get":{"summary":"List bank accounts","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Max items per page (default 50)"},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Pass the previous response's nextCursor to fetch the next page"},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"country","required":false,"schema":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"description":"Filter to a single country (IN | AU | US)"},{"in":"query","name":"customer_id","required":false,"schema":{"type":"string"},"description":"Filter by customer id"},{"in":"query","name":"type","required":false,"schema":{"type":"string"},"description":"Filter by account type"},{"in":"query","name":"status","required":false,"schema":{"type":"string","enum":["active","dormant","frozen","closed"]},"description":"Filter by status"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Account"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page"},"total":{"type":"integer","description":"Total documents matching the filter"}}}}}}},"description":"Deposit and current accounts held by Purple Retail Banking customers, with a self-describing balance, status (active | dormant | frozen | closed), branch and routing details. These are real bank accounts, not CRM accounts (companies) or ERP ledger accounts. Narrow results server-side with this endpoint's named filter params (country, customer_id, status, …) or with OData $filter, $orderby, $select, $search, $top, $skip and $count (MCP clients send those as _filter, _orderby, …) instead of paging through everything."},"post":{"summary":"Open bank account","parameters":[{"$ref":"#/components/parameters/InstanceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}}},"description":"Create a new deposit or current account for an existing banking customer. Use this to open an account, not to record a payment into one."}},"/bank_accounts/{id}":{"get":{"summary":"Get bank account","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"404":{"description":"Not found"}},"description":"Fetch one bank account by id (e.g. ACCT-0001) with its balance, status, freeze_reason, routing details and status_history — the record to check before freezing or explaining a decline."},"patch":{"summary":"Update bank account","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Fields to update (partial)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"404":{"description":"Not found"}},"description":"Partially update an account's mutable fields such as type, branch or routing. Do not change the status field here to stop activity: the freeze and unfreeze tools enforce the transition and write status_history."},"delete":{"summary":"Delete bank account","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string"}}}}}},"404":{"description":"Not found"}},"description":"Permanently delete a bank account record. Destructive and irreversible, and it strands the account's transactions and cards. To stop activity, freeze it; to close it properly, set status to 'closed'."}},"/bank_accounts/{id}/freeze":{"post":{"summary":"Freeze a bank account","description":"Stop all activity on a bank account: sets status to 'frozen' and records the reason in status_history. Use it for suspected fraud or a customer request covering the whole account; to stop just one card use the card block tool instead. Fails with 422 if the account is already frozen or closed. Reversible via unfreeze.","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","example":"Suspected unauthorised activity"}}}}}},"responses":{"200":{"description":"Frozen","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"404":{"description":"Not found"},"422":{"description":"Invalid state transition"}}}},"/bank_accounts/{id}/unfreeze":{"post":{"summary":"Unfreeze a bank account","description":"Return a frozen bank account to 'active' and record why, e.g. once a fraud review clears. Fails with 422 if the account is not currently frozen.","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string"}}}}}},"responses":{"200":{"description":"Unfrozen","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"404":{"description":"Not found"},"422":{"description":"Invalid state transition"}}}},"/transactions":{"get":{"summary":"List account transactions","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Max items per page (default 50)"},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Pass the previous response's nextCursor to fetch the next page"},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"country","required":false,"schema":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"description":"Filter to a single country (IN | AU | US)"},{"in":"query","name":"account_id","required":false,"schema":{"type":"string"},"description":"Filter by account id"},{"in":"query","name":"direction","required":false,"schema":{"type":"string","enum":["debit","credit"]},"description":"Filter by direction"},{"in":"query","name":"channel","required":false,"schema":{"type":"string"},"description":"Filter by channel"},{"in":"query","name":"status","required":false,"schema":{"type":"string","enum":["posted","pending","disputed","reversed"]},"description":"Filter by status"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Transaction"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page"},"total":{"type":"integer","description":"Total documents matching the filter"}}}}}}},"description":"Money movement on Purple Retail Banking accounts — card, UPI and transfer entries with a self-describing amount, direction (debit | credit), channel, counterparty, status and an is_flagged marker. Customer banking activity, not ERP invoices or employee expenses. Narrow results server-side with this endpoint's named filter params (country, customer_id, status, …) or with OData $filter, $orderby, $select, $search, $top, $skip and $count (MCP clients send those as _filter, _orderby, …) instead of paging through everything."},"post":{"summary":"Create transaction","parameters":[{"$ref":"#/components/parameters/InstanceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transaction"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transaction"}}}}},"description":"Write a transaction record against an account. This is a source-system record only: it does not move money and does not recompute the account balance."}},"/transactions/{id}":{"get":{"summary":"Get transaction","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transaction"}}}},"404":{"description":"Not found"}},"description":"Fetch one transaction by id (e.g. TXN-00001) with its amount, direction, channel, counterparty and status — the entry to cite when a customer queries or disputes a debit."},"patch":{"summary":"Update transaction","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Fields to update (partial)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transaction"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Transaction"}}}},"404":{"description":"Not found"}},"description":"Partially update a transaction, typically to mark it disputed or reversed, or to set is_flagged during a fraud review. The fraud alert and case records themselves live in Purple Risk & Compliance."},"delete":{"summary":"Delete transaction","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string"}}}}}},"404":{"description":"Not found"}},"description":"Permanently delete a transaction record. Destructive and irreversible, and it breaks the account's audit trail — model a real reversal by setting status to 'reversed' instead."}},"/cards":{"get":{"summary":"List cards","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Max items per page (default 50)"},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Pass the previous response's nextCursor to fetch the next page"},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"country","required":false,"schema":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"description":"Filter to a single country (IN | AU | US)"},{"in":"query","name":"customer_id","required":false,"schema":{"type":"string"},"description":"Filter by customer id"},{"in":"query","name":"account_id","required":false,"schema":{"type":"string"},"description":"Filter by account id"},{"in":"query","name":"status","required":false,"schema":{"type":"string","enum":["active","blocked","expired"]},"description":"Filter by status"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Card"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page"},"total":{"type":"integer","description":"Total documents matching the filter"}}}}}}},"description":"Debit and credit cards issued to Purple Retail Banking customers, with network, type, masked last_four, the account they draw on, limits and status (active | blocked | expired). Narrow results server-side with this endpoint's named filter params (country, customer_id, status, …) or with OData $filter, $orderby, $select, $search, $top, $skip and $count (MCP clients send those as _filter, _orderby, …) instead of paging through everything."},"post":{"summary":"Issue card","parameters":[{"$ref":"#/components/parameters/InstanceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Card"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Card"}}}}},"description":"Issue a new card to a banking customer and link it to one of their accounts. For a lost card, block the existing card first rather than only issuing a replacement."}},"/cards/{id}":{"get":{"summary":"Get card","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Card"}}}},"404":{"description":"Not found"}},"description":"Fetch one card by id with its network, type, last_four, linked account, limits and status — the record to check before blocking or explaining a declined payment."},"patch":{"summary":"Update card","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Fields to update (partial)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Card"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Card"}}}},"404":{"description":"Not found"}},"description":"Partially update card attributes such as limits, linked account or expiry. To stop a card, use the block tool rather than patching status: it enforces the transition and is reversible."},"delete":{"summary":"Delete card","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string"}}}}}},"404":{"description":"Not found"}},"description":"Permanently delete a card record. Destructive and irreversible — for a lost, stolen or compromised card, block it instead, which is reversible and preserves history."}},"/cards/{id}/block":{"post":{"summary":"Block a card","description":"Stop a single card by setting its status to 'blocked' — the usual response to lost, stolen or compromised plastic. Narrower than freezing the account: the account and the customer's other cards keep working. Fails with 422 if the card is already blocked or expired. Reversible via unblock.","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string"}}}}}},"responses":{"200":{"description":"Blocked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Card"}}}},"404":{"description":"Not found"},"422":{"description":"Invalid state transition"}}}},"/cards/{id}/unblock":{"post":{"summary":"Unblock a card","description":"Return a blocked card to 'active', e.g. after the customer finds it or a fraud review clears. Fails with 422 if the card is not currently blocked.","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Unblocked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Card"}}}},"404":{"description":"Not found"},"422":{"description":"Invalid state transition"}}}},"/products":{"get":{"summary":"List banking products","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Max items per page (default 50)"},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Pass the previous response's nextCursor to fetch the next page"},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"country","required":false,"schema":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"description":"Filter to a single country (IN | AU | US)"},{"in":"query","name":"category","required":false,"schema":{"type":"string","enum":["deposit","lending","card","investment","insurance"]},"description":"Filter by category"},{"in":"query","name":"active","required":false,"schema":{"type":"string"},"description":"Filter by active flag"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page"},"total":{"type":"integer","description":"Total documents matching the filter"}}}}}}},"description":"The bank's retail product catalogue (deposit | lending | card | investment | insurance) with interest rate, min/max amounts, eligibility summary and per-country availability. Catalogue definitions — nothing here is held by a specific customer. Narrow results server-side with this endpoint's named filter params (country, customer_id, status, …) or with OData $filter, $orderby, $select, $search, $top, $skip and $count (MCP clients send those as _filter, _orderby, …) instead of paging through everything."},"post":{"summary":"Create banking product","parameters":[{"$ref":"#/components/parameters/InstanceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}}},"description":"Add a product to the retail banking catalogue. This is not how a customer gets a product: that means opening an account, issuing a card, or booking a loan."}},"/products/{id}":{"get":{"summary":"Get banking product","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}},"404":{"description":"Not found"}},"description":"Fetch one catalogue product by id with its rate, amount limits, eligibility summary and country availability — the source for 'what does this product require' answers."},"patch":{"summary":"Update banking product","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Fields to update (partial)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Product"}}}},"404":{"description":"Not found"}},"description":"Update a catalogue product's commercial terms — rate, limits, eligibility text — or clear its active flag when the product is withdrawn."},"delete":{"summary":"Delete banking product","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string"}}}}}},"404":{"description":"Not found"}},"description":"Permanently delete a product from the catalogue. Destructive — if customers hold it, set active=false instead so existing accounts and loans still resolve."}},"/loans":{"get":{"summary":"List loans","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Max items per page (default 50)"},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Pass the previous response's nextCursor to fetch the next page"},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"country","required":false,"schema":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"description":"Filter to a single country (IN | AU | US)"},{"in":"query","name":"customer_id","required":false,"schema":{"type":"string"},"description":"Filter by customer id"},{"in":"query","name":"status","required":false,"schema":{"type":"string","enum":["active","delinquent","closed","written_off"]},"description":"Filter by status"},{"in":"query","name":"delinquency_bucket","required":false,"schema":{"type":"string","enum":["current","1-30","31-60","61-90","90+"]},"description":"Filter by delinquency bucket"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Loan"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page"},"total":{"type":"integer","description":"Total documents matching the filter"}}}}}}},"description":"Disbursed loans held by Purple Retail Banking customers, with principal and outstanding money, EMI, status (active | delinquent | closed | written_off), days past due and delinquency bucket. These are funded loans; applications still in origination are loan applications. Narrow results server-side with this endpoint's named filter params (country, customer_id, status, …) or with OData $filter, $orderby, $select, $search, $top, $skip and $count (MCP clients send those as _filter, _orderby, …) instead of paging through everything."},"post":{"summary":"Create loan","parameters":[{"$ref":"#/components/parameters/InstanceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Loan"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Loan"}}}}},"description":"Book a loan record for a customer. In the normal flow a loan comes into existence by disbursing an approved loan application, so check that pipeline before creating one directly."}},"/loans/{id}":{"get":{"summary":"Get loan","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Loan"}}}},"404":{"description":"Not found"}},"description":"Fetch one loan by id with its outstanding balance, EMI, dpd and delinquency bucket — the record behind payoff, arrears and collections questions."},"patch":{"summary":"Update loan","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Fields to update (partial)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Loan"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Loan"}}}},"404":{"description":"Not found"}},"description":"Partially update a loan — record repayment progress, refresh dpd and delinquency bucket, or change status as it cures or deteriorates."},"delete":{"summary":"Delete loan","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string"}}}}}},"404":{"description":"Not found"}},"description":"Permanently delete a loan record. Destructive and irreversible — a settled loan should be status 'closed' and an unrecoverable one 'written_off' so the credit history survives."}},"/loan_applications":{"get":{"summary":"List loan applications","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Max items per page (default 50)"},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Pass the previous response's nextCursor to fetch the next page"},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"country","required":false,"schema":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"description":"Filter to a single country (IN | AU | US)"},{"in":"query","name":"customer_id","required":false,"schema":{"type":"string"},"description":"Filter by customer id"},{"in":"query","name":"stage","required":false,"schema":{"type":"string","enum":["submitted","docs_pending","eligibility","underwriting","approved","rejected","disbursed"]},"description":"Filter by stage"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/LoanApplication"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page"},"total":{"type":"integer","description":"Total documents matching the filter"}}}}}}},"description":"Loan applications in origination, moving through submitted → docs_pending → eligibility → underwriting → approved → disbursed (or rejected), with requested amount, eligibility score and a required-document checklist. Nothing here is money lent yet — that is loans. Narrow results server-side with this endpoint's named filter params (country, customer_id, status, …) or with OData $filter, $orderby, $select, $search, $top, $skip and $count (MCP clients send those as _filter, _orderby, …) instead of paging through everything."},"post":{"summary":"Submit loan application","parameters":[{"$ref":"#/components/parameters/InstanceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoanApplication"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoanApplication"}}}}},"description":"Submit a new loan application for a customer against a lending product. It enters at the start of the pipeline; move it forward with the transition tool rather than by creating further applications."}},"/loan_applications/{id}":{"get":{"summary":"Get loan application","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoanApplication"}}}},"404":{"description":"Not found"}},"description":"Fetch one loan application by id with its current stage, eligibility score and per-document status — the check to run before advancing it or chasing paperwork."},"patch":{"summary":"Update loan application","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Fields to update (partial)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoanApplication"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoanApplication"}}}},"404":{"description":"Not found"}},"description":"Partially update application details such as requested amount, product or assigned underwriter. Do not set the stage field here: the transition tool enforces the pipeline, and document status has its own tool."},"delete":{"summary":"Delete loan application","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string"}}}}}},"404":{"description":"Not found"}},"description":"Permanently delete a loan application. Destructive and irreversible — a declined case should be transitioned to 'rejected' so the decision and its reason survive."}},"/loan_applications/{id}/documents":{"post":{"summary":"Set loan application document status","description":"Mark one required document on a loan application as missing, received or verified, adding it to the checklist if it is not already listed. This is the paperwork step that clears the docs_pending stage; it does not itself advance the application — use the transition tool for that.","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"doc_type":{"type":"string","example":"income_proof"},"status":{"type":"string","enum":["missing","received","verified"]}},"required":["doc_type","status"]}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoanApplication"}}}},"400":{"description":"Invalid body"},"404":{"description":"Not found"}}}},"/loan_applications/{id}/transition":{"post":{"summary":"Advance a loan application","description":"Move a loan application along the origination pipeline: submitted → docs_pending → eligibility → underwriting → approved → disbursed, with 'rejected' allowed from any pre-decision stage. Use this rather than patching the stage field: skipping or reversing stages is rejected with 422, and 'rejected' and 'disbursed' are terminal credit decisions that cannot be undone.","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"stage":{"type":"string","enum":["submitted","docs_pending","eligibility","underwriting","approved","rejected","disbursed"]},"reason":{"type":"string"}},"required":["stage"]}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoanApplication"}}}},"400":{"description":"Invalid stage"},"404":{"description":"Not found"},"422":{"description":"Invalid state transition"}}}},"/interactions":{"get":{"summary":"List customer interactions","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Max items per page (default 50)"},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Pass the previous response's nextCursor to fetch the next page"},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"country","required":false,"schema":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"description":"Filter to a single country (IN | AU | US)"},{"in":"query","name":"customer_id","required":false,"schema":{"type":"string"},"description":"Filter by customer id"},{"in":"query","name":"type","required":false,"schema":{"type":"string","enum":["call","meeting","note","visit"]},"description":"Filter by type"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Interaction"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page"},"total":{"type":"integer","description":"Total documents matching the filter"}}}}}}},"description":"Servicing touchpoints logged with a banking customer — calls, meetings, notes and branch visits — with subject, channel and who handled it. Relationship history inside the bank; distinct from CRM activities (B2B sales) and from complaints. Narrow results server-side with this endpoint's named filter params (country, customer_id, status, …) or with OData $filter, $orderby, $select, $search, $top, $skip and $count (MCP clients send those as _filter, _orderby, …) instead of paging through everything."},"post":{"summary":"Log customer interaction","parameters":[{"$ref":"#/components/parameters/InstanceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Interaction"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Interaction"}}}}},"description":"Log a servicing touchpoint (call, meeting, note or visit) against a banking customer. Use this for a contact record; if the customer is raising a grievance, log a complaint instead."}},"/interactions/{id}":{"get":{"summary":"Get customer interaction","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Interaction"}}}},"404":{"description":"Not found"}},"description":"Fetch one logged interaction by id with its type, subject, notes, handler and timestamp."},"patch":{"summary":"Update customer interaction","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Fields to update (partial)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Interaction"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Interaction"}}}},"404":{"description":"Not found"}},"description":"Correct or extend a logged interaction — its subject, notes or outcome."},"delete":{"summary":"Delete customer interaction","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string"}}}}}},"404":{"description":"Not found"}},"description":"Permanently delete a logged interaction, removing it from the customer's contact history. Destructive and irreversible."}},"/bank_opportunities":{"get":{"summary":"List banking opportunities","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Max items per page (default 50)"},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Pass the previous response's nextCursor to fetch the next page"},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"country","required":false,"schema":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"description":"Filter to a single country (IN | AU | US)"},{"in":"query","name":"customer_id","required":false,"schema":{"type":"string"},"description":"Filter by customer id"},{"in":"query","name":"product_category","required":false,"schema":{"type":"string","enum":["deposit","lending","card","investment","insurance"]},"description":"Filter by product category"},{"in":"query","name":"stage","required":false,"schema":{"type":"string","enum":["identified","proposed","accepted","declined"]},"description":"Filter by stage"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Opportunity"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page"},"total":{"type":"integer","description":"Total documents matching the filter"}}}}}}},"description":"Next-best-action product opportunities identified for a banking customer (deposit | lending | card | investment | insurance) with expected value, stage and a recommendation reason. Retail cross-sell prompts for a relationship manager, not CRM sales opportunities (B2B deals). Narrow results server-side with this endpoint's named filter params (country, customer_id, status, …) or with OData $filter, $orderby, $select, $search, $top, $skip and $count (MCP clients send those as _filter, _orderby, …) instead of paging through everything."},"post":{"summary":"Create banking opportunity","parameters":[{"$ref":"#/components/parameters/InstanceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Opportunity"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Opportunity"}}}}},"description":"Record a next-best-action product opportunity for a banking customer, with the product category, expected value and why it is being recommended."}},"/bank_opportunities/{id}":{"get":{"summary":"Get banking opportunity","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Opportunity"}}}},"404":{"description":"Not found"}},"description":"Fetch one banking opportunity by id with its product category, value, stage and recommendation reason."},"patch":{"summary":"Update banking opportunity","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Fields to update (partial)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Opportunity"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Opportunity"}}}},"404":{"description":"Not found"}},"description":"Move an opportunity along identified → proposed → accepted | declined, or revise its value and reasoning."},"delete":{"summary":"Delete banking opportunity","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string"}}}}}},"404":{"description":"Not found"}},"description":"Permanently delete a banking opportunity. Destructive — when a customer says no, set the stage to 'declined' instead so the outcome is retained."}},"/complaints":{"get":{"summary":"List customer complaints","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Max items per page (default 50)"},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Pass the previous response's nextCursor to fetch the next page"},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"country","required":false,"schema":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"description":"Filter to a single country (IN | AU | US)"},{"in":"query","name":"customer_id","required":false,"schema":{"type":"string"},"description":"Filter by customer id"},{"in":"query","name":"category","required":false,"schema":{"type":"string","enum":["payments","cards","loans","accounts","atm","digital"]},"description":"Filter by category"},{"in":"query","name":"status","required":false,"schema":{"type":"string","enum":["logged","investigating","resolved","closed"]},"description":"Filter by status"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Complaint"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page"},"total":{"type":"integer","description":"Total documents matching the filter"}}}}}}},"description":"Grievances customers have raised against the bank (payments, cards, loans, accounts, atm, digital), each with status logged | investigating | resolved | closed and an SLA clock. Regulated banking complaints, not IT incidents and not fraud cases. Narrow results server-side with this endpoint's named filter params (country, customer_id, status, …) or with OData $filter, $orderby, $select, $search, $top, $skip and $count (MCP clients send those as _filter, _orderby, …) instead of paging through everything."},"post":{"summary":"Log customer complaint","parameters":[{"$ref":"#/components/parameters/InstanceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Complaint"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Complaint"}}}}},"description":"Log a new complaint a customer has raised against the bank, with category and description. It starts at 'logged'; move it on with the transition tool."}},"/complaints/{id}":{"get":{"summary":"Get customer complaint","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Complaint"}}}},"404":{"description":"Not found"}},"description":"Fetch one complaint by id with its category, status, resolution notes and SLA timing — the record to check before promising a resolution date."},"patch":{"summary":"Update customer complaint","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Fields to update (partial)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Complaint"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Complaint"}}}},"404":{"description":"Not found"}},"description":"Partially update complaint details such as category, description, owner or notes. Use the transition tool for the status lifecycle: it enforces the required order."},"delete":{"summary":"Delete customer complaint","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string"}}}}}},"404":{"description":"Not found"}},"description":"Permanently delete a complaint. Destructive and irreversible, and it erases regulated complaint history — resolve and close it instead."}},"/complaints/{id}/transition":{"post":{"summary":"Transition a complaint","description":"Move a customer complaint through logged → investigating → resolved → closed, optionally recording resolution notes. Use this rather than patching status: a complaint must be 'resolved' before it can be 'closed', and 'closed' is terminal — a closed complaint cannot be reopened.","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["logged","investigating","resolved","closed"]},"resolution_notes":{"type":"string"}},"required":["status"]}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Complaint"}}}},"400":{"description":"Invalid status"},"404":{"description":"Not found"},"422":{"description":"Invalid state transition"}}}},"/known_issues":{"get":{"summary":"List banking known issues","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Max items per page (default 50)"},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Pass the previous response's nextCursor to fetch the next page"},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"country","required":false,"schema":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"description":"Filter to a single country (IN | AU | US)"},{"in":"query","name":"status","required":false,"schema":{"type":"string","enum":["known_error","resolved"]},"description":"Filter by status"},{"in":"query","name":"affected_service","required":false,"schema":{"type":"string"},"description":"Filter by affected service"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/KnownIssue"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page"},"total":{"type":"integer","description":"Total documents matching the filter"}}}}}}},"description":"Bank-wide known errors and service outages (e.g. a UPI/NPCI outage) with the affected service, customer-facing workaround and status. Check here before investigating one customer's failed payment — it is often already a known issue. Narrow results server-side with this endpoint's named filter params (country, customer_id, status, …) or with OData $filter, $orderby, $select, $search, $top, $skip and $count (MCP clients send those as _filter, _orderby, …) instead of paging through everything."},"post":{"summary":"Create banking known issue","parameters":[{"$ref":"#/components/parameters/InstanceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnownIssue"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnownIssue"}}}}},"description":"Publish a known issue or outage affecting a banking service, including the workaround agents should give customers."}},"/known_issues/{id}":{"get":{"summary":"Get banking known issue","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnownIssue"}}}},"404":{"description":"Not found"}},"description":"Fetch one known issue by id with the affected service, workaround, status and when it started."},"patch":{"summary":"Update banking known issue","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Fields to update (partial)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnownIssue"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnownIssue"}}}},"404":{"description":"Not found"}},"description":"Update a known issue — refine the workaround, or set status to 'resolved' once the service recovers."},"delete":{"summary":"Delete banking known issue","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string"}}}}}},"404":{"description":"Not found"}},"description":"Permanently delete a known issue record. Destructive — mark it 'resolved' instead so agents can still see that the outage happened."}},"/bank_sla_policies":{"get":{"summary":"List banking SLA policies","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"query","name":"limit","schema":{"type":"integer","default":50},"description":"Max items per page (default 50)"},{"in":"query","name":"cursor","schema":{"type":"string"},"description":"Pass the previous response's nextCursor to fetch the next page"},{"in":"query","name":"$filter","schema":{"type":"string"},"description":"OData filter expression. Comparison operators: eq, ne, gt, ge, lt, le. Logical: and, or, not, with parentheses for grouping. String functions: contains(field,'x'), startswith(field,'x'), endswith(field,'x'). Membership: field in ('a','b'). Case-insensitive matching via tolower(field) / toupper(field). Nested fields use a slash, e.g. pto_balance/vacation_days. String literals use single quotes; dates are ISO-8601. Examples: \"status eq 'active'\"; \"salary gt 50000 and department eq 'Engineering'\"; \"contains(tolower(name),'acme')\"; \"closeDate gt 2026-01-01\"."},{"in":"query","name":"$select","schema":{"type":"string"},"description":"Comma-separated list of fields to return, e.g. \"id,firstName,email\". Nested fields via slash (\"pto_balance/vacation_days\"). The id field is always included."},{"in":"query","name":"$orderby","schema":{"type":"string"},"description":"Sort clause(s), comma-separated, each \"field [asc|desc]\" (asc is the default). Example: \"createdAt desc, lastName asc\". Null values sort last."},{"in":"query","name":"$search","schema":{"type":"string"},"description":"Free-text, case-insensitive substring match across the resource's searchable fields."},{"in":"query","name":"$top","schema":{"type":"integer"},"description":"Maximum number of items to return (page size)."},{"in":"query","name":"$skip","schema":{"type":"integer"},"description":"Number of matching items to skip before returning results (offset paging). Combine with $top."},{"in":"query","name":"$count","schema":{"type":"boolean"},"description":"When true, include @odata.count (total matches after $filter/$search, before paging) in the response."},{"in":"query","name":"country","required":false,"schema":{"type":"string","enum":["IN","AU","US","GB","KR","DE"]},"description":"Filter to a single country (IN | AU | US)"},{"in":"query","name":"category","required":false,"schema":{"type":"string","enum":["payments","cards","loans","accounts","atm","digital"]},"description":"Filter by category"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SlaPolicy"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page, or null on the last page"},"total":{"type":"integer","description":"Total documents matching the filter"}}}}}}},"description":"The bank's service-level commitments by country and category (payments, cards, loans, accounts, atm, digital), giving response and resolution hours and whether a regulatory clock applies. Use these to judge whether a complaint is breaching. Not ITSM SLAs. Narrow results server-side with this endpoint's named filter params (country, customer_id, status, …) or with OData $filter, $orderby, $select, $search, $top, $skip and $count (MCP clients send those as _filter, _orderby, …) instead of paging through everything."},"post":{"summary":"Create banking SLA policy","parameters":[{"$ref":"#/components/parameters/InstanceId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlaPolicy"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlaPolicy"}}}}},"description":"Define a new banking service-level policy for a country and category, with response/resolution hours and the regulatory-clock flag."}},"/bank_sla_policies/{id}":{"get":{"summary":"Get banking SLA policy","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlaPolicy"}}}},"404":{"description":"Not found"}},"description":"Fetch one banking SLA policy by id with its response and resolution hours and regulatory-clock flag."},"patch":{"summary":"Update banking SLA policy","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Fields to update (partial)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlaPolicy"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlaPolicy"}}}},"404":{"description":"Not found"}},"description":"Change a banking SLA policy's response or resolution hours, or its regulatory-clock flag."},"delete":{"summary":"Delete banking SLA policy","parameters":[{"$ref":"#/components/parameters/InstanceId"},{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"id":{"type":"string"}}}}}},"404":{"description":"Not found"}},"description":"Permanently delete a banking SLA policy. Destructive — complaints in that category then have no target to measure against."}}}}