# Create Invoice Source: https://developer.meow.com/api-reference/billing/create-invoice POST /billing/invoices Creates a new invoice. # Create Invoicing Customer Source: https://developer.meow.com/api-reference/billing/create-invoicing-customer POST /billing/customers Creates a new invoicing customer. # Create Product Source: https://developer.meow.com/api-reference/billing/create-product POST /billing/products Creates a new product. # Download Invoice PDF Source: https://developer.meow.com/api-reference/billing/download-invoice-pdf GET /billing/invoices/{invoice_id}/download Downloads an invoice as a PDF file. # Get a Specific Line Item for an Invoice Source: https://developer.meow.com/api-reference/billing/get-a-specific-line-item-for-an-invoice GET /billing/invoices/{invoice_id}/line-items/{line_item_id} Returns a specific line item by ID for a given invoice. # Get Invoice Source: https://developer.meow.com/api-reference/billing/get-invoice GET /billing/invoices/{invoice_id} Gets an invoice by ID. # Get Invoicing Customer by ID Source: https://developer.meow.com/api-reference/billing/get-invoicing-customer-by-id GET /billing/customers/{customer_id} Returns an invoicing customer by their ID. # Get Payment Method Types Source: https://developer.meow.com/api-reference/billing/get-payment-method-types GET /billing/payment-method-types Returns the payment method types available to you. # Get Product by ID Source: https://developer.meow.com/api-reference/billing/get-product-by-id GET /billing/products/{product_id} Returns a product by its ID. # List All Line Items for an Invoice Source: https://developer.meow.com/api-reference/billing/list-all-line-items-for-an-invoice GET /billing/invoices/{invoice_id}/line-items Returns all line items associated with a specific invoice. # List Collection Accounts Source: https://developer.meow.com/api-reference/billing/list-collection-accounts GET /billing/accounts Returns the collection accounts you can use to collect invoice payments. Only open, unlocked accounts are returned, with your primary account first. # List Invoices Source: https://developer.meow.com/api-reference/billing/list-invoices GET /billing/invoices Lists all invoices. # List Invoicing Customers Source: https://developer.meow.com/api-reference/billing/list-invoicing-customers GET /billing/customers Returns a list of all invoicing customers. # List Products Source: https://developer.meow.com/api-reference/billing/list-products GET /billing/products Returns a list of all products to be used with invoicing. # Confirm Bill Documents Source: https://developer.meow.com/api-reference/billpay/confirm-bill-documents POST /bills/{bill_id}/documents Attaches documents to the bill after you have uploaded them to the URLs from the presigned-urls endpoint. # Create Bill Source: https://developer.meow.com/api-reference/billpay/create-bill POST /bills Creates a bill as a draft for approval. The bill is not paid until someone approves it in the Meow dashboard; this never moves money on its own. Drafting is capped at 500 bills per rolling 24 hours per creator; creates past the cap return `429 Too Many Requests`. # Create Bill Document Upload URLs Source: https://developer.meow.com/api-reference/billpay/create-bill-document-upload-urls POST /bills/{bill_id}/documents/presigned-urls Returns short-lived upload URLs for attaching documents (for example, an invoice) to a bill. Upload each file to its URL, then call the confirm endpoint. # Delete Bill Source: https://developer.meow.com/api-reference/billpay/delete-bill DELETE /bills/{bill_id} Cancels a bill that is still a draft. Once it has been submitted for approval it can no longer be canceled here, whether or not anyone has approved it yet. # Get Bill Source: https://developer.meow.com/api-reference/billpay/get-bill GET /bills/{bill_id} Returns details for a specific bill by ID, including all line items. # List Bills Source: https://developer.meow.com/api-reference/billpay/list-bills GET /bills Returns a paginated list of bills with optional status filtering. # Update Bill Source: https://developer.meow.com/api-reference/billpay/update-bill PATCH /bills/{bill_id} Updates an editable bill. Only the fields you send are changed, and the bill stays a draft pending approval; this never moves money. # Close a Bank Account Source: https://developer.meow.com/api-reference/core-api/close-account POST /accounts/{account_id}/close Closes a bank account. The account must have a zero balance and no active cards, scheduled payments, or open invoices. Cancel or remove those first. Supported for checking accounts (grasshopper, tcb, firstbank) and crb accounts. Some accounts close immediately (status `closed`); others process the close asynchronously (status `close_pending`), in which case poll `GET /accounts/{account_id}` until its `status` reads `CLOSED`. # Create ACH Transfer Source: https://developer.meow.com/api-reference/core-api/create-ach-transfer POST /accounts/{account_id}/ach Creates a new ACH transfer to an external counterparty. # Create Book Transfer Source: https://developer.meow.com/api-reference/core-api/create-book-transfer POST /accounts/{account_id}/book Creates a new internal book transfer between bank accounts. # Create Card Source: https://developer.meow.com/api-reference/core-api/create-card POST /cards Creates a virtual card with a spend limit. By default the card authorizes at any merchant up to that limit. Pass `spending_restriction` with merchant IDs from `GET /cards/merchants` to restrict it, and `allowed_categories` to restrict by merchant category. Set `single_use` to `true` (default) for a one-shot card that auto-cancels after the first approved authorization, or `false` for a multi-use card that stays active until expiry or explicit revoke. Retrieve the PAN by calling `POST /cards/{card_id}/pan`. # Create Contact Source: https://developer.meow.com/api-reference/core-api/create-contact POST /contacts Creates a new contact with domestic (ACH, wire, or check) payment details, international payment details, or one or more crypto addresses. # Create Crypto Transaction Source: https://developer.meow.com/api-reference/core-api/create-crypto-transaction POST /accounts/{account_id}/crypto Creates a new crypto transaction from a specified account. The currency and network are determined automatically from the destination's payment method. Provide exactly one of `static_memo_id` or `crypto_id` to identify the destination: - **`static_memo_id`**: Use the ID from the contact's `payment_methods.static_memos` array. Supported for both banking and wallet accounts. - **`crypto_id`**: Use the ID from the contact's `payment_methods.crypto` array. Supported for both banking and wallet accounts. Supported currencies: `usdc`, `usdt`, `pyusd`, `cash`, `usdg`. Requires the `transfers:crypto:write` scope. Use this endpoint for all crypto transfers. The legacy `/usdc` endpoint is deprecated. # Create International Payment Source: https://developer.meow.com/api-reference/core-api/create-international-payment POST /accounts/{account_id}/international Pays a contact in their local currency, converting from US dollars at the rate quoted when you call. Fix either leg: give `recipient_amount` to send an exact amount in their currency, or `source_amount` to spend an exact number of US dollars. Preview the rate and fee first with `POST /accounts/{account_id}/international/quote`. The contact must already have international payment details saved. To send US dollars instead, including to a bank outside the US, use `POST /accounts/{account_id}/wire`. # Create Liquidation Address Source: https://developer.meow.com/api-reference/core-api/create-liquidation-address POST /accounts/{account_id}/liquidation-addresses Creates a blockchain address that converts incoming cryptocurrency to USD and deposits it into the specified bank account. The business must have crypto deposits enabled, and the account must support them. Repeating the same account, network, and currency returns the existing address. # Create Scheduled ACH Transfer Source: https://developer.meow.com/api-reference/core-api/create-scheduled-ach-transfer POST /accounts/{account_id}/ach/scheduled Creates a new scheduled ACH transfer to an external counterparty. # Create USDC Transaction Source: https://developer.meow.com/api-reference/core-api/create-usdc-transaction POST /accounts/{account_id}/usdc Legacy endpoint for USDC transactions. Requires the `transfers:usdc:write` scope. For new integrations, use `POST /accounts/{account_id}/crypto`. The destination determines the currency and network. # Create Wire Transfer Source: https://developer.meow.com/api-reference/core-api/create-wire-transfer POST /accounts/{account_id}/wire Creates a new wire transfer to an external counterparty. Wires are sent in US dollars, including to a counterparty banking outside the US. To pay someone in their own currency, use `POST /accounts/{account_id}/international`. # Delete a Contact Source: https://developer.meow.com/api-reference/core-api/delete-a-contact DELETE /contacts/{contact_id} Deletes a contact. # Get Account Details Source: https://developer.meow.com/api-reference/core-api/get-account-details GET /accounts/{account_id} Returns detailed information about a specified bank account. # Get ACH Transfer Source: https://developer.meow.com/api-reference/core-api/get-ach-transfer GET /accounts/{account_id}/achs/{ach_transfer_id} Returns details about an ACH transfer (incoming or outgoing) for the specified account by ACH transfer ID, including the status of an outgoing ACH transfer after it has been initiated. # Get Card Source: https://developer.meow.com/api-reference/core-api/get-card GET /cards/{card_id} Returns details of a specific card you have access to. # Get Card Details Source: https://developer.meow.com/api-reference/core-api/get-card-details GET /cards/{card_id}/details Returns operational details of a card: billing address, delivery address, linked banking account, and shipping tracking URL. # Get Card Limits Source: https://developer.meow.com/api-reference/core-api/get-card-limits GET /cards/{card_id}/limits Returns a card's configured spend limits and, where the issuer reports it, how much of each limit is left. A null `remaining` means the issuer does not report it for this card, not that the limit is used up. To change these limits, send `spending_controls` to Update Card. # Get Card PAN Source: https://developer.meow.com/api-reference/core-api/get-card-pan POST /cards/{card_id}/pan Claims the PAN for a card created via the API and returns a short-lived reveal grant. GET the `reveal_url` with your API key and `Authorization: Bearer ` to receive the card number, CVC, and expiration. Requires the `cards:write` scope. Pass the `card_id` returned by Create Card. This can be called only once per card; subsequent calls return an error. A vendor failure before the reveal grant is created does not consume the one-time retrieval, so that request can be retried. Revoke and recreate the card if the PAN is needed again after a successful call. # Get Card Spending Insights Source: https://developer.meow.com/api-reference/core-api/get-card-spending-insights GET /cards/insights Returns entity-wide spending analytics for your entity: total spend, cashback earned, top merchants, top expenses, and weekly trends. Requires admin, viewer, or card manager access. # Get Card Transaction Source: https://developer.meow.com/api-reference/core-api/get-card-transaction GET /cards/transactions/{transaction_id} Returns one entry from a card's transaction history, by the `id` returned in List Card Transactions or in Get Card Spending Insights. An entry is one step of a purchase, so to see the whole purchase filter List Card Transactions by this entry's `trace_id`. # Get Check Deposit Source: https://developer.meow.com/api-reference/core-api/get-check-deposit GET /accounts/{account_id}/check-deposits/{transaction_id} Returns details about a check deposit for the specified account by transaction ID. Includes check number and originating bank details useful for reconciliation. # Get Contact Source: https://developer.meow.com/api-reference/core-api/get-contact GET /contacts/{contact_id} Returns details of a specific contact. # Get Crypto Transaction Source: https://developer.meow.com/api-reference/core-api/get-crypto-transaction GET /accounts/{account_id}/crypto/{transaction_id} Returns details about a crypto transaction (incoming or outgoing) for the specified account by transaction ID, including the status of an outgoing crypto transfer after it has been initiated, and the sending address for an incoming deposit. # Get Current API Key Source: https://developer.meow.com/api-reference/core-api/get-current-api-key GET /api-keys/current Returns metadata about the API key used for the current request, including its type and scopes. # Get Daily Withdrawal Limit Source: https://developer.meow.com/api-reference/core-api/get-daily-withdrawal-limit GET /limits/daily-withdrawal Returns your current daily withdrawal limit, the highest you can raise it to, and how much you have withdrawn and can still withdraw today. # Get International Contact Requirements Source: https://developer.meow.com/api-reference/core-api/get-international-contact-requirements GET /contacts/international-requirements Returns the fields an international contact needs before you create one, for a given currency and destination bank country. Destinations differ: some route on an IBAN, others on a domestic routing code, and some want the recipient's full address. Ask here first, then send the answer's fields as `international` on `POST /contacts`. Check `supported` before reading the fields, since not every currency reaches every country. # Get USDC Transaction Source: https://developer.meow.com/api-reference/core-api/get-usdc-transaction GET /accounts/{account_id}/transactions/{transaction_id} Legacy endpoint for retrieving USDC transaction details. For new integrations, use `GET /accounts/{account_id}/crypto/{transaction_id}`. # Get Wire Transfer Source: https://developer.meow.com/api-reference/core-api/get-wire-transfer GET /accounts/{account_id}/wires/{wire_transfer_id} Returns details about a wire transfer (incoming or outgoing) for the specified account by wire transfer ID, including the status of an outgoing wire after it has been initiated. # List Accessible Entities Source: https://developer.meow.com/api-reference/core-api/list-accessible-entities GET /api-keys/accessible-entities Returns the entities your API key can access, with their IDs and names, so you know which entity ID to pass in the `x-entity-id` header. # List Balances for an Account Source: https://developer.meow.com/api-reference/core-api/list-balances-for-an-account GET /accounts/{account_id}/balances Returns a list of balances for a specified account. # List Bank Accounts Source: https://developer.meow.com/api-reference/core-api/list-bank-accounts GET /accounts Returns a list of bank accounts. # List Card Transactions Source: https://developer.meow.com/api-reference/core-api/list-card-transactions GET /cards/transactions Returns transactions for the cards you have access to. A purchase produces several entries: an authorization when the merchant places the hold, a clearing when the money moves, and a refund or reversal if it comes back. Entries that share a `trace_id` are the same purchase; it can be absent, in which case an entry stands alone. Every amount is positive and `type` carries the direction, so to total actual spend take the clearing entries minus the refund and reversal entries: an authorization and its clearing are the same money, so counting both doubles the purchase. Filter by card, status, merchant, date range, or amount. # List Cards Source: https://developer.meow.com/api-reference/core-api/list-cards GET /cards Returns the cards you have access to. As an admin or viewer you see all entity cards; as a card manager you see your team's cards; as a card user you see only your own. # List Contacts Source: https://developer.meow.com/api-reference/core-api/list-contacts GET /contacts Returns a paginated list of contacts, ordered by nickname. Use the `limit` and `offset` query parameters to page through results. # List Feature Access Requests Source: https://developer.meow.com/api-reference/core-api/list-feature-access-requests GET /feature-access/requests Returns your restricted-feature access requests and the status of each one. # List Merchants Source: https://developer.meow.com/api-reference/core-api/list-merchants GET /cards/merchants Returns merchants that can be used in a card's `spending_restriction`. Filter by name with `query`. Pass the returned `id` values to Create Card or Update Card. # List Payment Networks for an Account Source: https://developer.meow.com/api-reference/core-api/list-payment-networks-for-an-account GET /accounts/{account_id}/payment-networks Returns the payment networks supported by a specified account. # List Scheduled ACH Transfers Source: https://developer.meow.com/api-reference/core-api/list-scheduled-ach-transfers GET /accounts/{account_id}/ach/scheduled Returns a list of scheduled ACH transfers for a specified account. # List Transactions for an Account Source: https://developer.meow.com/api-reference/core-api/list-transactions-for-an-account GET /accounts/{account_id}/transactions Returns a list of transactions for a specified account. # Open an Additional Bank Account Source: https://developer.meow.com/api-reference/core-api/open-account POST /accounts Opens an additional bank account under the given `product`. You must already have an open account of that product, and your business must have completed verification and accepted its required agreements. See `consent` on the entity's onboarding status. The account may open immediately (status `created`, with `account_id`) or in the background (status `processing`); when `processing`, poll `GET /accounts` until the account appears. # Quote an International Payment Source: https://developer.meow.com/api-reference/core-api/quote-international-payment POST /accounts/{account_id}/international/quote Prices an international payment without sending it, so you can show the rate, the fee, and the total debit before committing. Fix either leg: give `recipient_amount` to learn what the payment costs you, or `source_amount` to learn what the recipient gets. Nothing is reserved, and the payment is priced again when you call `POST /accounts/{account_id}/international`, so the figures can move if the rate does. # Request Feature Access Source: https://developer.meow.com/api-reference/core-api/request-feature-access POST /feature-access/requests Requests access to a restricted feature, such as international payments. Send the eligibility details for the feature and we notify the team that reviews them. Eligible businesses may be approved right away. If you have already requested this feature, we return its current status. # Retrieve Tax Form Source: https://developer.meow.com/api-reference/core-api/retrieve-tax-form GET /tax-forms/{tax_form_id} Returns a time-limited download URL for the tax form. Use a `tax_form_id` from the search endpoint; the value is opaque, so do not construct it yourself. # Reveal Card PAN Source: https://developer.meow.com/api-reference/core-api/reveal-card-pan GET /cards/{card_id}/pan/reveal Returns the card number, CVC, and expiration for a claimed card. Call this via the `reveal_url` returned by Get Card PAN with `Authorization: Bearer `, not against the API origin directly, which returns tokenized placeholders. Requires the `cards:write` scope and a valid reveal token. # Revoke Card Source: https://developer.meow.com/api-reference/core-api/revoke-card POST /cards/{card_id}/revoke Immediately revoke a card. Revoke a card whose purchase failed to close it before it expires. # Search Tax Forms Source: https://developer.meow.com/api-reference/core-api/search-tax-forms GET /tax-forms Returns IRS tax forms (the 1099 family) issued for the specified account. Currently supports `Tax1099Int` for bank accounts; brokerage and wallet accounts return an empty list until additional form types are exposed. # Set Daily Withdrawal Limit Source: https://developer.meow.com/api-reference/core-api/set-daily-withdrawal-limit PUT /limits/daily-withdrawal Sets the most that can be withdrawn from your accounts in a single day across all transfer types. Must be a whole dollar amount from 0 up to the maximum set for your entity. Set it to 0 to block all outbound transfers while you collect funds, then raise it when you are ready to pay out. # Update a Bank Account Source: https://developer.meow.com/api-reference/core-api/update-account PATCH /accounts/{account_id} Updates a bank account's `nickname` (display name), shown in the dashboard to distinguish it from other accounts. Supported for checking accounts (grasshopper, tcb, firstbank) and crb accounts. The nickname must be unique among your accounts. # Update Card Source: https://developer.meow.com/api-reference/core-api/update-card PATCH /cards/{card_id} Updates a card you created via the API. Freeze or unfreeze it with the `status` field, replace its spend limits with `spending_controls`, or change its merchant restriction with `spending_restriction` and its allowed merchant categories. All fields are optional; omitted fields are left unchanged. Read the current limits back from Get Card Limits. # Update Contact Source: https://developer.meow.com/api-reference/core-api/update-contact PATCH /contacts/{contact_id} Updates an existing contact and optionally adds new payment methods, including further crypto addresses. # Validate Routing Number Source: https://developer.meow.com/api-reference/core-api/validate-routing-number GET /routing-numbers/{routing_number} Validates a routing number and returns bank information including name, address, and supported payment methods (ACH/Wire). # Add a representative Source: https://developer.meow.com/api-reference/onboarding/add-representative POST /entities/{entity_id}/representatives Add a representative (a beneficial owner or officer) to a business entity. Set `is_primary` to mark the primary representative (signer); exactly one primary is required before the application can be submitted. Verify the representative separately by submitting their KYC data or minting a self-serve verification link. This is allowed only before the application is submitted for review. # Confirm a document upload Source: https://developer.meow.com/api-reference/onboarding/confirm-document POST /entities/{entity_id}/documents/confirm Confirm that a KYB document was uploaded successfully. Pass the same `representative_id` given when the upload URL was generated (if any) to record which representative the document belongs to; it is echoed back on the entity's onboarding status under `documents`. # Confirm an information request document Source: https://developer.meow.com/api-reference/onboarding/confirm-info-request-document POST /entities/{entity_id}/info-requests/{request_id}/documents/confirm Confirm that a document for an info request was uploaded successfully. The document is attributed to `request_id`, so it counts against what that request is waiting on. `request_completed` reports whether this satisfied the request in full; a request also stays open on unanswered follow-up questions, which `next_step.info_requests[].questions_pending` flags. # Create a business entity Source: https://developer.meow.com/api-reference/onboarding/create-entity POST /entities Creates a new business entity owned by your API key's user. Send an empty body to create a bare not-started entity, or seed any KYB business details up front: legal name, incorporation, the registered legal and (optional) physical address, and the business profile fields (phone, tax ID, website, description, industry, account purpose, and digital-currency questions). The creating user is granted admin on the new entity, so the same API key can immediately act on it by passing the returned entity ID in the `x-entity-id` header. Anything you omit can be added later with Update Business Details. The entity still completes KYB (beneficial owners and any remaining details) before it can be approved or open bank accounts. # Create a representative verification link Source: https://developer.meow.com/api-reference/onboarding/create-verification-link POST /entities/{entity_id}/representatives/{representative_id}/verification-link Mint a shareable identity-verification link the representative opens to complete KYC themselves. The alternative to submitting their KYC data directly. # Get due diligence requirements Source: https://developer.meow.com/api-reference/onboarding/get-due-diligence-requirements GET /entities/{entity_id}/due-diligence List the enhanced due diligence forms required for a business entity, each with its completion status and whether it must be completed in the web app. Fetch each submittable form's schema, then submit it. # Get a due diligence form schema Source: https://developer.meow.com/api-reference/onboarding/get-due-diligence-schema GET /entities/{entity_id}/due-diligence/schema/{form_type} Get the field schema for a due diligence form: its pages and elements with types, choices, validation, and conditional visibility. Every standalone form with a schema can be submitted through the API. Use the schema to build a valid submission body. # Get an information request form Source: https://developer.meow.com/api-reference/onboarding/get-info-request-schema GET /entities/{entity_id}/info-requests/{request_id}/schema The form describing everything an information request collects. Documents and follow-up questions are elements of one schema: a `file_upload` element carries the `proof_type` to pass to the document upload endpoints, and the remaining elements are questions to answer. Take `request_id` from `next_step.info_requests[].request_id`. # Get an information request upload URL Source: https://developer.meow.com/api-reference/onboarding/get-info-request-upload-url POST /entities/{entity_id}/info-requests/{request_id}/documents/upload-url Get a pre-signed URL to upload a document a reviewer asked for after the application was submitted. Take `request_id` from `next_step.info_requests[]` on the onboarding status, and the `proof_type` from that entry's `required_proof_types`. Use HTTP PUT to upload the file to the returned URL, then confirm it. Unlike `/documents/upload-url`, this stays available after submission, which is when info requests are raised. # Get onboarding status Source: https://developer.meow.com/api-reference/onboarding/get-onboarding-status GET /entities/{entity_id} Returns the onboarding status of a business entity, including uploaded documents and each representative's KYC status in the `representatives` array. The top-level `kyc_status` reflects the primary representative. # Get a document upload URL Source: https://developer.meow.com/api-reference/onboarding/get-upload-url POST /entities/{entity_id}/documents/upload-url Get a pre-signed URL to upload a KYB document. Use HTTP PUT to upload the file to the returned URL, then confirm it. For a document that belongs to a specific representative (e.g. their ID or proof of address), pass their `representative_id` here and again on the confirm call, which records the link. # List onboarding entities Source: https://developer.meow.com/api-reference/onboarding/list-entities GET /entities Lists the business entities being onboarded by your API key's user. # List representatives Source: https://developer.meow.com/api-reference/onboarding/list-representatives GET /entities/{entity_id}/representatives List the representatives (beneficial owners and officers) on a business entity's application, each with their identity verification (KYC) status. The primary representative (signer) is returned first. Use each `representative_id` to submit KYC, mint a verification link, or make it primary. # Make a representative primary Source: https://developer.meow.com/api-reference/onboarding/make-representative-primary POST /entities/{entity_id}/representatives/{representative_id}/make-primary Make an existing representative the primary representative (signer). Any current primary is demoted, so exactly one remains. Allowed only before the application is submitted for review. Returns the updated onboarding status. # Submit a due diligence form Source: https://developer.meow.com/api-reference/onboarding/submit-due-diligence POST /entities/{entity_id}/due-diligence Submit an enhanced due diligence form for a business entity. Some industries require additional due diligence before the application can be submitted (check `next_step.missing` on the onboarding status). Every standalone form with a schema can be submitted here. Supporting documents a form refers to are uploaded separately as entity documents, not in this body. Your answers are checked against the form schema, so fetch it first and send everything it marks required. Returns the updated onboarding status. # Submit Entity Application Source: https://developer.meow.com/api-reference/onboarding/submit-entity-application POST /entities/{entity_id}/submit Submit a fully prepared business entity for review. Choose the checking account product to open. The application must have everything it needs first (check `next_step.can_submit` on the onboarding status); otherwise this returns the outstanding requirements. Once submitted, the application moves to review and can no longer be edited. Pass `product_features` to request additional products alongside the checking account (for example `stablecoins` for USDC access); they are enabled once the application is approved. Returns the updated onboarding status, including a `consent_url`: share it with the primary representative so they can review and accept the required agreements, disclosures, and attestations in their browser (no login needed). The same link is also returned on `GET /entities/{entity_id}` after submission. # Resolve an information request Source: https://developer.meow.com/api-reference/onboarding/submit-info-request-answers POST /entities/{entity_id}/info-requests/{request_id} Answer an information request's follow-up questions. Answers are validated against the request's form schema and submitted in one call. There is no draft state on this API. Documents are uploaded separately; `outstanding` reports whatever the request still owes. # Submit representative KYC data Source: https://developer.meow.com/api-reference/onboarding/submit-representative-kyc POST /entities/{entity_id}/representatives/{representative_id}/kyc Submit identity (KYC) data for a representative you added. Verification runs server-side; poll the entity onboarding status endpoint for the result. Use this when you hold the representative's identity details; otherwise mint a verification link for them to self-verify. # Update Business Details Source: https://developer.meow.com/api-reference/onboarding/update-business-details PATCH /entities/{entity_id}/business-details Update a business entity's KYB details before its application is submitted. Send only the fields you want to change; anything you omit keeps its current value. Use this to fill in the business phone, tax ID, website, description, industry, account purpose, and digital-currency questions, and the registered legal and physical addresses, or to correct details captured at creation. Send `physical_address` together with `business_address`; omitting it keeps any physical address already on file, or uses the legal address when none is set. Returns the refreshed onboarding status, including what is still required next. # Meow API reference: endpoints, payloads, and examples Source: https://developer.meow.com/api-reference/overview Complete reference for Meow API endpoints covering accounts, balances, transfers, contacts, billing, invoicing, partner onboarding, and routing checks. ## Overview The Meow API provides comprehensive financial services and billing capabilities for businesses. ### Core Features * **Financial Data Access**: Securely access account information, transactions, balances, and payment networks * **USDC Operations**: Create and manage cryptocurrency contacts, initiate USDC transfers across multiple blockchain networks ### Billing API The Billing API enables businesses to streamline their invoicing and payment collection: * **Product Management**: Create and manage products with customizable pricing * **Customer Management**: Maintain invoicing customers with billing addresses * **Invoice Creation**: Generate invoices with line items, discounts, and custom notes * **Flexible Payment Options**: Accept payments via bank transfer, ACH, cards, international wires, and USDC * **Collection Accounts**: Configure accounts for automated payment collection ## Authentication All API requests require an API key passed in the `x-api-key` header. See the [Authentication](/authentication) guide for details. ## Base URLs | Environment | URL | | ----------- | --------------------------------- | | Production | `https://api.meow.com/v1` | | Sandbox | `https://api.sandbox.meow.com/v1` | | Development | `https://api.dev.meow.com/v1` | ## Multi-Entity Access For API keys with access to multiple entities, include the `x-entity-id` header to scope requests to a specific entity. # Confirm Document Upload Source: https://developer.meow.com/api-reference/partner-onboarding/confirm-document POST /partner/applications/{app_id}/documents/confirm Confirm that a KYB document was uploaded successfully. # Create Application Source: https://developer.meow.com/api-reference/partner-onboarding/create-application POST /partner/applications Create a new onboarding application with business details. The applicant receives an invitation email to set their password and complete onboarding. # Get Application Status Source: https://developer.meow.com/api-reference/partner-onboarding/get-application GET /partner/applications/{app_id} Get the onboarding status of an application, including uploaded documents and the applicant's KYC status. # Get Document Upload URL Source: https://developer.meow.com/api-reference/partner-onboarding/get-upload-url POST /partner/applications/{app_id}/documents/upload-url Get a pre-signed URL to upload a KYB document. Use HTTP PUT to upload the file to the returned URL, then confirm it. # List Applications Source: https://developer.meow.com/api-reference/partner-onboarding/list-applications GET /partner/applications List all applications you have created. # Submit KYC Data Source: https://developer.meow.com/api-reference/partner-onboarding/submit-kyc POST /partner/applications/{app_id}/kyc Submit identity (KYC) data you have already collected for the applicant, with their consent. Verification runs server-side; the applicant does not need to complete a verification link. The result is usually available asynchronously: poll the application status endpoint for the final `kyc_status`, or subscribe to the `identity_verification.*` webhook events. Repeat submissions while a verification is in progress are idempotent and return the current status without re-sending data; corrected data is applied on a new submission after the current attempt completes or fails. # Create Partner Webhook Subscription Source: https://developer.meow.com/api-reference/partner-webhooks/create-subscription POST /partner/webhooks/subscriptions Creates a partner webhook subscription and returns it along with the signing secret. The secret is returned only once, so store it now to verify delivery signatures. # Delete Partner Webhook Subscription Source: https://developer.meow.com/api-reference/partner-webhooks/delete-subscription DELETE /partner/webhooks/subscriptions/{subscription_id} Deletes a partner webhook subscription. # List Partner Webhook Deliveries Source: https://developer.meow.com/api-reference/partner-webhooks/list-deliveries GET /partner/webhooks/deliveries Returns your partner webhook deliveries across all subscriptions, most recent first. # List Partner Delivery Attempts Source: https://developer.meow.com/api-reference/partner-webhooks/list-delivery-attempts GET /partner/webhooks/deliveries/{delivery_id}/attempts Returns the individual delivery attempts for a partner webhook delivery, including the response status of each attempt. # List Partner Webhook Subscriptions Source: https://developer.meow.com/api-reference/partner-webhooks/list-subscriptions GET /partner/webhooks/subscriptions Returns your partner webhook subscriptions. # Redrive Partner Delivery Source: https://developer.meow.com/api-reference/partner-webhooks/redrive-delivery POST /partner/webhooks/deliveries/{delivery_id}/redrive Re-queues a failed partner webhook delivery for another attempt. A delivery that already succeeded or is currently being attempted cannot be redriven. # Send Partner Test Event Source: https://developer.meow.com/api-reference/partner-webhooks/send-test-event POST /partner/webhooks/subscriptions/{subscription_id}/test Sends a test event to a partner webhook subscription and queues it for delivery so you can verify your endpoint and signature handling. # Update Partner Webhook Subscription Source: https://developer.meow.com/api-reference/partner-webhooks/update-subscription PATCH /partner/webhooks/subscriptions/{subscription_id} Updates a partner webhook subscription. Returns the updated subscription, including a new signing secret when you request a rotation. # Get Simulated Inbound Transfer Status Source: https://developer.meow.com/api-reference/sandbox/get-simulated-inbound-transfer-status GET /simulations/inbound_transfers/{sender_name} > ⚠️ **Sandbox & development only.** Simulation endpoints are not available in production and return `404 Not Found` there. Checks whether a simulated inbound transfer has settled into a transaction yet. Pass the `sender_name` returned by Simulate Inbound Transfer. Returns `processing` until the transfer settles, and `settled` with the resulting `transaction_id` once it does. # Simulate Application Approval Source: https://developer.meow.com/api-reference/sandbox/simulate-application-approval POST /simulations/entities/{entity_id}/application_approval > ⚠️ **Sandbox & development only.** Simulation endpoints are not available in production and return `404 Not Found` there. Approves a submitted onboarding application in the sandbox as if the compliance review had passed, and kicks off checking-account onboarding for the selected product. Every representative must have passed identity verification (`kyc_status` is `approved`) first: like real review, the simulation will not approve an application whose beneficial owner failed KYC, and returns `400` until they pass. Requires an entity API key scoped to the requested entity, or a global API key with the `simulations:write` scope whose user administers the entity; the application must already be submitted. The application status moves to `submitted` immediately and to `approved` once the checking account activates. Poll `GET /entities/{entity_id}` or subscribe to `application.*` webhooks to observe the transition. Safe to call repeatedly: if account opening stalls in the sandbox, calling this again completes it. # Simulate Card Authorization Source: https://developer.meow.com/api-reference/sandbox/simulate-card-authorization POST /simulations/card_authorizations > ⚠️ **Sandbox & development only.** Simulation endpoints are not available in production and return `404 Not Found` there. Simulates a card authorization in the sandbox and returns the resulting authorization, its status, and whether it was approved. # Simulate Card Authorization Capture Source: https://developer.meow.com/api-reference/sandbox/simulate-card-authorization-capture POST /simulations/card_authorizations/{authorization_id}/capture > ⚠️ **Sandbox & development only.** Simulation endpoints are not available in production and return `404 Not Found` there. Simulates capturing a previously simulated card authorization in the sandbox. Pass the `authorization_id` returned when you created the authorization. # Simulate Card Authorization Increment Source: https://developer.meow.com/api-reference/sandbox/simulate-card-authorization-increment POST /simulations/card_authorizations/{authorization_id}/increment > ⚠️ **Sandbox & development only.** Simulation endpoints are not available in production and return `404 Not Found` there. Simulates incrementing the amount of a previously simulated card authorization in the sandbox. Pass the `authorization_id` returned when you created the authorization. # Simulate Card Authorization Reversal Source: https://developer.meow.com/api-reference/sandbox/simulate-card-authorization-reversal POST /simulations/card_authorizations/{authorization_id}/reverse > ⚠️ **Sandbox & development only.** Simulation endpoints are not available in production and return `404 Not Found` there. Simulates reversing a previously simulated card authorization in the sandbox. Pass the `authorization_id` returned when you created the authorization. # Simulate Card Transaction Refund Source: https://developer.meow.com/api-reference/sandbox/simulate-card-transaction-refund POST /simulations/card_transactions/{transaction_id}/refund > ⚠️ **Sandbox & development only.** Simulation endpoints are not available in production and return `404 Not Found` there. Simulates refunding a previously simulated card transaction in the sandbox. Pass the `transaction_id` of the transaction to refund. # Simulate Inbound Transfer Source: https://developer.meow.com/api-reference/sandbox/simulate-inbound-transfer POST /simulations/inbound_transfers > ⚠️ **Sandbox & development only.** Simulation endpoints are not available in production and return `404 Not Found` there. Simulates an inbound ACH, wire, RTP, or FedNow transfer into a cash account in the sandbox. Only cash accounts support this simulation. # Simulate Stablecoin Account Funding Source: https://developer.meow.com/api-reference/sandbox/simulate-stablecoin-account-funding POST /simulations/fund_financial_account > ⚠️ **Sandbox & development only.** Simulation endpoints are not available in production and return `404 Not Found` there. Simulates funding your stablecoin account in the sandbox so you can test stablecoin flows with available balance. # Create Global Webhook Subscription Source: https://developer.meow.com/api-reference/webhooks-global/create-subscription POST /webhooks/global/subscriptions Creates a webhook subscription for every entity your global API key's user administers, including entities added later. A global subscription is owned by your API key's user, not a single entity. It delivers events for **every entity that user administers**, including entities added later, so you register once and never re-subscribe as your access grows. Each delivery carries the originating entity in `entity_id` so you can route it to the right tenant. # Delete Global Webhook Subscription Source: https://developer.meow.com/api-reference/webhooks-global/delete-subscription DELETE /webhooks/global/subscriptions/{subscription_id} Deletes a global webhook subscription. Its delivery history stays queryable so you can review past attempts. # List Global Webhook Deliveries Source: https://developer.meow.com/api-reference/webhooks-global/list-deliveries GET /webhooks/global/deliveries Returns webhook deliveries across all global subscriptions for your global API key user, most recent first. Deliveries across every subscription owned by your global API key's user, most recent first. Each row carries `entity_id`, the entity the event belongs to, so you can demux a single global stream back to individual entities, alongside the `event_type`, resource (`resource_object` / `resource_id`), and `sequence`. # List Global Webhook Delivery Attempts Source: https://developer.meow.com/api-reference/webhooks-global/list-delivery-attempts GET /webhooks/global/deliveries/{delivery_id}/attempts Returns the individual delivery attempts for a global webhook delivery, including the response status of each attempt. # List Global Webhook Subscriptions Source: https://developer.meow.com/api-reference/webhooks-global/list-subscriptions GET /webhooks/global/subscriptions Returns webhook subscriptions configured for your global API key user. # Redrive Global Webhook Delivery Source: https://developer.meow.com/api-reference/webhooks-global/redrive-delivery POST /webhooks/global/deliveries/{delivery_id}/redrive Re-queues a failed global webhook delivery for another attempt. A delivery that already succeeded or is currently being attempted cannot be redriven. # Send Global Test Webhook Event Source: https://developer.meow.com/api-reference/webhooks-global/send-test-event POST /webhooks/global/subscriptions/{subscription_id}/test Sends a test event to a global webhook subscription and queues it for delivery so you can verify your endpoint and signature handling. # Update Global Webhook Subscription Source: https://developer.meow.com/api-reference/webhooks-global/update-subscription PATCH /webhooks/global/subscriptions/{subscription_id} Updates a global webhook subscription. Returns the updated subscription, including a new signing secret when you request a rotation. # Create Webhook Subscription Source: https://developer.meow.com/api-reference/webhooks/create-subscription POST /webhooks/subscriptions Creates a webhook subscription for your entity and returns it along with the signing secret. The secret is returned only once, so store it now to verify delivery signatures. # Delete Webhook Subscription Source: https://developer.meow.com/api-reference/webhooks/delete-subscription DELETE /webhooks/subscriptions/{subscription_id} Deletes a webhook subscription. Its delivery history stays queryable so you can review past attempts. Deletion takes effect immediately. Fanout stops and the subscription no longer appears in [list](/api-reference/webhooks/list-subscriptions). It does not return; create a new subscription to resume. The deleted endpoint's past [delivery history](/api-reference/webhooks/list-deliveries) is retained for audit, and the same URL can be used for a new subscription afterward. # Meow webhook event types and payload reference Source: https://developer.meow.com/api-reference/webhooks/events Reference for every Meow webhook event type, including transfer, card, invoice, and bill notifications with their payload schemas and status values. Every event Meow sends has the same envelope. The `data` object is the resource snapshot (or `{id, object}` in `thin` mode). The lifecycle stage lives on `data.status`. Dispatch on that, not the event name. ```json Envelope theme={null} { "type": "ach_transfer.updated", "timestamp": "2026-04-28T08:00:00Z", "data": { "...": "..." }, "sequence": 4 } ``` **`sequence`** is a per-resource counter for discarding stale deliveries. See [Handle out-of-order deliveries](/guides/webhooks#5-handle-out-of-order-deliveries). It rides resource events and is absent on `webhook.test`, `message.attempt.exhausted`, and partner `application.*` events. **ID shapes.** * Meow-managed IDs (`entity_id`, `counterparty_id`, `virtual_account_id`, `subscription_id`, `delivery_id`) are UUIDs. Account IDs are opaque strings such as `cash_account_` or `bridge_wallet_`. * Resource IDs (`ach_transfer.id`, `wire_transfer.id`, inbound transaction `id`, `check_deposit.id`, `global_account_transfer.id`, `crypto_transfer.id`) are **opaque strings whose format varies by the underlying bank**: * CRB / FirstBank: `deposit_inach_11mn5s777xgveb9`, `withdrawal_txc_15wp3bd309xenf6p`, `book_11n0b3vs9vy0ypw`, `interest_txc_15wp2ghq09qm3141`. * Bridge global accounts: `bridge_va_inach_` (incoming ACH), `bridge_va_iw_` (incoming wire), `bridge_wallet_ext_ach_` (external ACH off-ramp), `bridge_wallet_ext_wire_` (external wire), `wallet_transfer_group_` / `wallet_deposit_group_` (wallet send / receive). * Treasury Prime: TP's own external IDs. Treat resource IDs as tokens; don't parse them. | Event | When it fires | | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ach_transfer.created` | A new outbound ACH transfer was created. | | `ach_transfer.updated` | An outbound ACH transfer changed state. | | `wire_transfer.created` | A new outbound wire was created. | | `wire_transfer.updated` | An outbound wire changed state. | | `book_transfer.created` | A transfer between two accounts at the same bank was created. | | `book_transfer.updated` | A book transfer changed state. | | `inbound_ach_transfer.created` | An ACH credit or debit hit your account. | | `inbound_ach_transfer.updated` | An inbound ACH changed state. | | `inbound_wire_transfer.created` | A wire arrived at your account. | | `inbound_wire_transfer.updated` | An inbound wire changed state. | | `check_deposit.created` | A check deposit was submitted. | | `check_deposit.updated` | A check deposit changed state. | | `global_account_transfer.created` | Activity on a global (virtual) account. Single-emission. | | `crypto_transfer.created` | A crypto transfer was first observed. | | `crypto_transfer.updated` | An inbound or outbound crypto transfer changed state. | | `card.created` | A card was issued. | | `card.updated` | A card changed state. | | `account.created` | A Meow deposit account is live and addressable on `GET /accounts`. For Treasury Prime this fires when the bank confirms the application, later than the `POST /accounts` call that returned `status=processing`. | | `account.updated` | A Meow deposit account's status changed, including closes. | | `info_request.created` | A reviewer asked a submitted business for more documents or answers. | | `info_request.updated` | An information request's outstanding set changed, or it was satisfied. | | `onboarding_consent.updated` | The agreements a business owes changed: they became owed at submission, the form was opened, or an item was accepted. | | `webhook.test` | A test event you triggered. | | `message.attempt.exhausted` | A delivery hit 10 failed attempts and the subscription was auto-disabled. | **Onboarding events** (`application.*`, `identity_verification.*`) describe an application's lifecycle and go to whoever owns that application's stream. If a partner created it, they go to that partner's subscription. If you onboarded the business yourself through the API with a global key, they go to **your own entity subscription**. You do not need a partner subscription to receive them. So the two catalogs overlap on the onboarding events rather than being disjoint. An entity subscription may name the rail events **and** the onboarding events; a partner subscription may name only the onboarding events. Listing an event a subscription could never receive returns a 400. | Event | When it fires | | --------------------------------------- | ---------------------------------------------------- | | `application.created` | A partner created an onboarding application. | | `application.under_review` | KYB review is pending. | | `application.submitted` | KYB approved; the banking account is not yet active. | | `application.approved` | Onboarding finished and the account is active. | | `application.rejected` | The application was rejected. | | `identity_verification.action_required` | A representative has to verify their identity again. | | `identity_verification.approved` | A representative passed identity verification. | | `identity_verification.rejected` | A representative was denied. | ## ach\_transfer `status` values: `pending`, `processing`, `sent`, `returned`, `canceled`, `error`. Optional fields not shown in the sample: `operation_request_id`. ```json theme={null} { "type": "ach_transfer.updated", "timestamp": "2026-04-28T08:00:00Z", "data": { "id": "withdrawal_txc_15wp3bd309xenf6p", "object": "ach_transfer", "created": "2026-04-28T07:59:50Z", "updated": "2026-04-28T08:00:00Z", "entity_id": "6f3b1d7c-aa90-4f0c-a5d2-9c8b1d3e2c10", "status": "sent", "amount": "1500.00", "counterparty_id": "5dada3c2-7f3a-4f2e-8a91-3d6f1c9b9e22", "counterparty_name": "Acme Corp", "counterparty_account_number": "****1234", "counterparty_routing_number": "021000021", "description": "April invoice", "error": null } } ``` ## wire\_transfer Same `status` values as `ach_transfer`. Extra wire-only fields: `counterparty_bank_name`, `purpose`, `imad` (Fed reference), `instructions`. ```json theme={null} { "type": "wire_transfer.updated", "timestamp": "2026-04-28T08:00:00Z", "data": { "id": "wire_dom_a8e9c7d24b1f4a3e8a91d9e87c5b9001", "object": "wire_transfer", "created": "2026-04-28T07:59:50Z", "updated": "2026-04-28T08:00:00Z", "entity_id": "6f3b1d7c-aa90-4f0c-a5d2-9c8b1d3e2c10", "status": "sent", "amount": "10000.00", "counterparty_id": "5dada3c2-7f3a-4f2e-8a91-3d6f1c9b9e22", "counterparty_name": "Acme Corp", "counterparty_bank_name": "JPMorgan Chase", "purpose": "Q2 settlement", "imad": "20260428B1QGC07C012345" } } ``` ## book\_transfer A transfer between two accounts at the same bank. Emitted only for the debited (source) business, using the same outbound framing as `ach_transfer` and `wire_transfer`, so the credited side does not get a copy. Internal legs Meow moves on your behalf (FX funding, insured cash sweep, core migrations) are suppressed. `status` values: `pending`, `sent`, `canceled`, `error`. Book transfers use a narrower set than `ach_transfer` / `wire_transfer`: there is no `processing`, `returned`, or `pending_approval` leg. Both sides are Meow accounts, so there are no counterparty routing details. `to_entity_id` is the business that was credited, including your own when you move money between two of your accounts. It is `null` when Meow cannot resolve the receiving account. ```json theme={null} { "type": "book_transfer.updated", "timestamp": "2026-04-28T08:00:00Z", "data": { "id": "book_txc_28fp4ce417yqmg2r", "object": "book_transfer", "created": "2026-04-28T07:59:50Z", "updated": "2026-04-28T08:00:00Z", "entity_id": "6f3b1d7c-aa90-4f0c-a5d2-9c8b1d3e2c10", "status": "sent", "amount": "2500.00", "description": "Move to payroll", "error": null, "to_entity_id": null } } ``` ## inbound\_ach\_transfer `status` values: `pending`, `posted`, `scheduled`, `failed`, `canceled`, `returned`. Casing matches outbound (lowercase on the wire). ```json theme={null} { "type": "inbound_ach_transfer.updated", "timestamp": "2026-04-28T14:00:00Z", "data": { "id": "deposit_inach_11mn5s777xgveb9", "object": "inbound_ach_transfer", "created": "2026-04-28T13:59:00Z", "updated": "2026-04-28T14:00:00Z", "entity_id": "6f3b1d7c-aa90-4f0c-a5d2-9c8b1d3e2c10", "account_id": "a8e9c7d2-4b1f-4a3e-8a91-d9e87c5b9001", "amount": "250.00", "currency": "USD", "status": "posted", "originator_name": "Stripe Connect" } } ``` ## inbound\_wire\_transfer Same shape as inbound ACH; `object` is `inbound_wire_transfer`. ## check\_deposit Same shape as inbound ACH; `object` is `check_deposit`. ## global\_account\_transfer Activity on a global (virtual) account. Each activity is one-shot: `funds_scheduled` and `funds_received` for the same deposit share `deposit_id` but arrive as two separate events with different `id`s. `activity_type` values: `funds_scheduled`, `funds_received`, `payment_submitted`, `payment_processed`, `refund_in_flight`, `refunded`, `refund_failed`. `currency` is lowercase: `usd`, `usdc`, `usdt`, `usdb`, `usdg`, `pyusd`, `dai`, `cash`. ```json theme={null} { "type": "global_account_transfer.created", "timestamp": "2026-04-28T08:00:00Z", "data": { "id": "bridge_va_inach_f5140b06-814e-4f4b-810b-217859d7b721", "object": "global_account_transfer", "created": "2026-04-28T08:00:00Z", "updated": null, "entity_id": "6f3b1d7c-aa90-4f0c-a5d2-9c8b1d3e2c10", "virtual_account_id": "9b3a7c5e-1d20-4a6f-8c1d-e7b34f25a8e9", "activity_type": "funds_received", "amount": "5000.00", "currency": "usdc", "payment_rail": null } } ``` ## crypto\_transfer An inbound deposit or outbound crypto send. The first observed state emits `crypto_transfer.created`. Later status changes emit `crypto_transfer.updated`. Several provider states can map to one public status, so not every provider callback produces an event. `direction` is `inbound` or `outbound`. `status` moves through: | Status | Meaning | | ------------ | ----------------------------------------------------------------------------------------------------------------------- | | `pending` | The transfer is known but the funds have not been sent yet. | | `processing` | The payment has been submitted on chain. | | `completed` | The funds reached the destination. | | `refunding` | Delivery failed or the payment was reversed. A refund is owed and has not landed yet. | | `returned` | The refund reached the sender. This is the terminal state of a refund, and the only status that says the money is back. | | `canceled` | The transfer was canceled. | | `failed` | The transfer failed and no refund is in progress. | A reversed transfer reports `refunding` first and `returned` only once the refund settles, so treat `refunding` as in-flight and wait for `returned` before reconciling the money as returned. Several provider states collapse onto `refunding`, so you may receive it once for a sequence of provider callbacks. `amount` and `currency` describe the cryptocurrency transferred on chain. For a send funded with dollars, `amount` is the recipient amount after fees, not the dollar amount debited from the source account. For inbound transfers, `destination` is `global_account` or `bank_account`. `source_tx_hash` and `source_address` identify the on-chain source when available. Use `source_tx_hash` as the `deposit_tx_hash` query value on `GET /accounts/{account_id}/transactions` to reconcile the resulting account transaction. The fiat leg of a `bank_account` deposit does not emit `inbound_wire_transfer.*`. For outbound transfers, `destination` and all source fields are null. `destination_address`, `destination_tx_hash`, and `destination_address_nickname` describe the recipient. `destination_tx_hash` stays null until Bridge reports it. Outbound events cover global-account sends and direct bank-funded sends. Legacy bank-funded static-memo sends continue to use wire webhooks. `account_id` identifies the account that sends or receives the funds. Subscriptions are entity-scoped, so use this field to route the event. ### Inbound example ```json theme={null} { "type": "crypto_transfer.created", "timestamp": "2026-04-28T08:00:00Z", "data": { "object": "crypto_transfer", "id": "f5140b06-814e-4f4b-810b-217859d7b721", "entity_id": "6f3b1d7c-aa90-4f0c-a5d2-9c8b1d3e2c10", "account_id": "bridge_wallet_a8e9c7d2-4b1f-4a3e-8a91-d9e87c5b9001", "direction": "inbound", "created": "2026-04-28T08:00:00Z", "updated": null, "amount": "500.00", "currency": "usdc", "network": "ethereum", "status": "pending", "destination": "global_account", "source_tx_hash": "0xabc123def456789...", "source_address": "0x1a2b3c4d5e6f...", "destination_address": null, "destination_tx_hash": null, "destination_address_nickname": null }, "sequence": 1 } ``` ### Outbound example ```json theme={null} { "type": "crypto_transfer.created", "timestamp": "2026-04-28T08:00:00Z", "data": { "object": "crypto_transfer", "id": "3f8d54e8-7ed0-4a79-9cf7-a12d8e57cf02", "entity_id": "6f3b1d7c-aa90-4f0c-a5d2-9c8b1d3e2c10", "account_id": "bridge_wallet_a8e9c7d2-4b1f-4a3e-8a91-d9e87c5b9001", "direction": "outbound", "created": "2026-04-28T08:00:00Z", "updated": null, "status": "pending", "amount": "100.00", "currency": "usdc", "network": "ethereum", "destination": null, "source_tx_hash": null, "source_address": null, "destination_address": "0x1234567890abcdef1234567890abcdef12345678", "destination_tx_hash": null, "destination_address_nickname": "Vendor Wallet" }, "sequence": 1 } ``` ## card A corporate card. `card.created` fires when a card is issued; `card.updated` fires on every subsequent change. Dispatch on `data.status`, not the event type — every state change, including freeze and unfreeze, arrives as `card.updated`. `status` values: `pending` (issuer is provisioning the card), `active`, `suspended` (frozen or under a fraud hold), `closed`, `failed`. `last_four` reads `****` while the card is still provisioning. `spending_restriction` is set when the card is restricted to or blocked from a specific merchant list. `allowed_categories` narrows the card to specific MCC categories; null means every category is allowed. ```json theme={null} { "type": "card.created", "timestamp": "2026-04-28T08:00:00Z", "data": { "object": "card", "id": "card_f5140b06814e4f4b810b217859d7b721", "entity_id": "6f3b1d7c-aa90-4f0c-a5d2-9c8b1d3e2c10", "created": "2026-04-28T08:00:00Z", "updated": null, "status": "active", "display_name": "Ops team card", "last_four": "4242", "is_physical": false, "is_single_use": false, "expiration": "2028-04-30T00:00:00Z", "spending_restriction": null, "allowed_categories": null, "cardholder": { "name": "Felix Chen", "public_id": "b4c7e2a1-5f38-4d9b-8a2c-7e1f3d6b8c05" } }, "sequence": 1 } ``` ## account A Meow deposit account. `account.created` fires when the account is live and addressable on `GET /accounts`. For Treasury Prime that is when the bank confirms the application, later than the `POST /accounts` call. `account.updated` fires on status changes, including closes. `status` values: `OPEN`, `PENDINGOPEN`, `PENDINGCLOSE`, `CLOSED`, `RESTRICTED`, `DELINQUENT`. `reference_id` is the value you sent on `POST /accounts`. It is how you match this event back to the request that opened the account, which matters most on the products that open in the background, where the create response carries no `account_id`. It is `null` if you did not send one. Balances are not included. They move independently of the account itself. Read them from `GET /accounts/{account_id}`. ```json theme={null} { "type": "account.created", "timestamp": "2026-04-28T08:00:00Z", "data": { "id": "cash_account_3f9c1b2e-7a4d-4e88-9c21-0b6f5d8e1a2c", "object": "account", "created": "2026-04-28T08:00:00Z", "updated": null, "entity_id": "6f3b1d7c-aa90-4f0c-a5d2-9c8b1d3e2c10", "status": "OPEN", "account_number_last_4": "4321", "bank_product_type": "Grasshopper Bank N.A.; Member FDIC", "nickname": "Business Checking", "reference_id": "order-2026-04-001" } } ``` ## info\_request A reviewer's request for more documents or answers, raised against a business that has already submitted. `info_request.created` fires when the request is raised. `info_request.updated` fires each time the outstanding set changes: a document lands, the questions are answered, and again when the request is satisfied. `outstanding` is everything the request is still waiting on, documents and questions alike. Each item carries `kind` (`document` or `answers`), the `proof_type` for a document, and a human-readable `detail`, so route on `kind` and `proof_type` rather than parsing the text. `required_proof_types` is the document half of the same list, as `proof_type` values the upload endpoints accept. `completed_at` is what says the request is done. It is null while anything is left. A document leaves `outstanding` when it is uploaded, and also when the business [explains that it cannot provide it](/guides/onboarding#when-the-business-cannot-provide-a-document). Both settle the document and both emit `info_request.updated`. Fetch the request's form with `GET /entities/{entity_id}/info-requests/{request_id}/schema` to see exactly what to collect. See the [onboarding guide](/guides/onboarding) for the full flow. Expiry emits no event. A request lapses when its `expires_at` passes, and nothing runs at that moment. Read `expires_at` and time it out yourself. ```json theme={null} { "type": "info_request.updated", "timestamp": "2026-08-11T09:20:14Z", "data": { "id": "6f3a1b2c-8d4e-4f10-9a2b-7c5e1d3f9a80", "object": "info_request", "created": "2026-08-11T09:02:00Z", "updated": "2026-08-11T09:20:14Z", "entity_id": "b1b2c3d4-aa90-4f0c-a5d2-9c8b1d3e2c10", "required_proof_types": [], "questions_pending": true, "outstanding": [ { "kind": "answers", "proof_type": null, "detail": "Source of funds is required" } ], "note": "Please send a recent bank statement", "expires_at": "2026-08-18T09:02:00Z", "completed_at": null } } ``` ## onboarding\_consent The agreements the business has to accept, tracked against the consent record that backs `consent_url`. `onboarding_consent.updated` fires when they first become owed at submission, when the representative opens the form, and on each item they accept. `remaining_items` is what is still owed and `consented_items` is what has been accepted. `status` moves `pending` to `viewed` to `completed`; `completed_at` is set once nothing remains. The event at submission is what tells you the agreements are owed at all. Every later event is driven by the representative acting on the form, so a business whose link is never opened produces no further events. Until `remaining_items` is empty, `POST /accounts` returns `403`. This family has no `.created`. Unlike the other resources, the consent record reports every stage through `onboarding_consent.updated`, including the first event at submission. Dispatch on `status` and `remaining_items`, not on the event name. ```json theme={null} { "type": "onboarding_consent.updated", "timestamp": "2026-08-11T09:02:00Z", "data": { "id": "9d7c6b5a-1e2f-4a3b-8c9d-0e1f2a3b4c5d", "object": "onboarding_consent", "created": "2026-08-11T09:02:00Z", "updated": "2026-08-11T09:02:00Z", "entity_id": "b1b2c3d4-aa90-4f0c-a5d2-9c8b1d3e2c10", "status": "pending", "consented_items": [], "remaining_items": [ "application_attestation", "meow_terms_of_service", "patriot_act" ], "viewed_at": null, "completed_at": null } } ``` ## webhook.test Triggered manually via `POST /webhooks/subscriptions/{id}/test`. Always sent in full (the `thin` mode falls through because there's no `id` to GET). ```json theme={null} { "type": "webhook.test", "timestamp": "2026-04-28T08:00:00Z", "data": { "subscription_id": "7b9e8a5f-3c2d-4d11-9a8c-1e6c4f2a5b30", "entity_id": "6f3b1d7c-aa90-4f0c-a5d2-9c8b1d3e2c10", "generated_at": "2026-04-28T08:00:00Z" } } ``` ## message.attempt.exhausted Fires when a delivery hits 10 failed attempts. The subscription is auto-disabled (`disabled_reason=retry_exhausted`). Use this to page an on-call. `last_response_status` is null if every attempt failed before getting an HTTP response (timeout, DNS, TLS). ```json theme={null} { "type": "message.attempt.exhausted", "timestamp": "2026-04-28T08:00:00Z", "data": { "delivery_id": "0c5b4a3e-9d2c-4f1a-b7e0-21b1d8c4a9f1", "subscription_id": "7b9e8a5f-3c2d-4d11-9a8c-1e6c4f2a5b30", "event_type": "ach_transfer.updated", "attempt_count": 10, "last_response_status": 504, "last_error_message": "Your endpoint did not respond within the timeout." } } ``` ## application Delivered to whoever owns the application's stream: the partner that created it, or, for an API self-serve onboarding opened with a global key, the onboarded business's own entity subscription. Each event reports an onboarding application reaching a new status; the event name and `data.status` always match. `application_id` is the application identifier (the KYB info public id); `entity_id` is the onboarded business, present on every event including `application.created`. `previous_status` is `null` only on `application.created`. `status` values: `pending_document_upload`, `pending_user_action`, `under_review`, `submitted`, `approved`, `rejected`. Note `submitted` sits *later* in the lifecycle than `under_review`: `under_review` means KYB review is pending, `submitted` means KYB approved but the account is not yet active. Unlike the rail events, `application.*` events carry **no `sequence`** and are always delivered as a full snapshot (partner subscriptions don't support `thin` mode). ```json theme={null} { "type": "application.approved", "timestamp": "2026-04-28T08:00:00Z", "data": { "application_id": "8f2a1c4d-3b9e-4a7c-9d18-2e6c4f0a5b31", "entity_id": "6f3b1d7c-aa90-4f0c-a5d2-9c8b1d3e2c10", "partner_reference_id": "your-internal-ref-001", "status": "approved", "previous_status": "submitted", "business_name": "Acme Corp" } } ``` ## identity\_verification Reports one representative's identity-verification status on an onboarding application. Delivered to the same owner as `application.*`: the partner that created the application, or, for an API self-serve onboarding opened with a global key, the onboarded business's own entity subscription. This is a separate family from the application-level `application.*` events; a subscriber can take one without the other. `status` is always `action_required`, `approved`, or `rejected`, mirroring the event name. `previous_status` may additionally be `pending`, and is `null` on the first notification. Only `approved` is final. `action_required` means the representative has to verify again (their verification expired or was canceled) and can legitimately repeat across attempts, and a `rejected` representative stays under reconciliation, so either may be followed by further events if they verify again. The payload carries normalized state only, never the underlying reason: no PEP, watchlist, or vendor failure detail is included. `representative_id` is the stable identifier; `representative_email` is the correlation key you submitted. Like `application.*`, these carry **no `sequence`** and are always a full snapshot. ```json theme={null} { "type": "identity_verification.action_required", "timestamp": "2026-04-28T08:00:00Z", "data": { "application_id": "8f2a1c4d-3b9e-4a7c-9d18-2e6c4f0a5b31", "entity_id": "6f3b1d7c-aa90-4f0c-a5d2-9c8b1d3e2c10", "representative_id": "b4c7e2a1-5f38-4d9b-8a2c-7e1f3d6b8c05", "representative_email": "felix@catnipcoffee.com", "partner_reference_id": "catnip-coffee-4471", "status": "action_required", "previous_status": "pending" } } ``` # List Webhook Deliveries Source: https://developer.meow.com/api-reference/webhooks/list-deliveries GET /webhooks/deliveries Returns the webhook deliveries for your entity across all subscriptions, most recent first. Each row names what the delivery carried: `event_type`, the resource it described (`resource_object` / `resource_id`), and its `sequence`, so you can find and [redrive](/api-reference/webhooks/redrive-delivery) a failed delivery without cross-referencing event IDs. The resource fields and `sequence` are `null` for events that describe no resource (`webhook.test`, `message.attempt.exhausted`). # List Webhook Delivery Attempts Source: https://developer.meow.com/api-reference/webhooks/list-delivery-attempts GET /webhooks/deliveries/{delivery_id}/attempts Returns the individual delivery attempts for a webhook delivery, including the response status of each attempt. # List Webhook Subscriptions Source: https://developer.meow.com/api-reference/webhooks/list-subscriptions GET /webhooks/subscriptions Returns the webhook subscriptions configured for your entity. # Redrive Webhook Delivery Source: https://developer.meow.com/api-reference/webhooks/redrive-delivery POST /webhooks/deliveries/{delivery_id}/redrive Re-queues a failed webhook delivery for another attempt. A delivery that already succeeded or is currently being attempted cannot be redriven. # Send Test Webhook Event Source: https://developer.meow.com/api-reference/webhooks/send-test-event POST /webhooks/subscriptions/{subscription_id}/test Sends a test event to a webhook subscription and queues it for delivery so you can verify your endpoint and signature handling. # Update Webhook Subscription Source: https://developer.meow.com/api-reference/webhooks/update-subscription PATCH /webhooks/subscriptions/{subscription_id} Updates a webhook subscription. Returns the updated subscription, including a new signing secret when you request a rotation. # Authenticate Meow API requests with x-api-key headers Source: https://developer.meow.com/authentication Authenticate Meow API requests with x-api-key, scope calls to a specific entity using x-entity-id, and manage API keys from the Meow dashboard. ## API Key Authentication The Meow API uses API key authentication. Include your API key in the `x-api-key` header with every request. ```bash theme={null} curl -H "x-api-key: YOUR_API_KEY" https://api.meow.com/v1/accounts ``` ## Obtaining an API Key You can create and manage API keys from the [Meow Dashboard](https://www.meow.com/dashboard). ## Scopes Each API key carries a set of scopes, and every endpoint requires the scope listed on its reference page. Choose the scopes a key needs when you create it in the dashboard; a request with a key that lacks the endpoint's scope is rejected. | Scope | Grants | | ------------------------------- | ---------------------------------------------------------------- | | `accounts:read` | Read account details | | `accounts:write` | Create and update accounts | | `accounts:balances` | Read account balances | | `accounts:transactions` | Read account transactions | | `accounts:payment-networks` | Read payment networks and routing details | | `accounts:statements` | Read account statements | | `accounts:tax-documents` | Read tax documents | | `banking:limits:read` | Read banking limits | | `banking:limits:write` | Request banking limit changes | | `billpay:read` | Read bills | | `billpay:write` | Create, update, and cancel bills, and attach documents | | `billing:accounts:read` | Read collection accounts | | `billing:customers:read` | Read invoicing customers | | `billing:customers:write` | Create and update invoicing customers | | `billing:invoices:read` | Read invoices | | `billing:invoices:write` | Create and update invoices | | `billing:products:read` | Read products | | `billing:products:write` | Create and update products | | `cards:read` | Read cards, card transactions, and insights | | `cards:write` | Create, update, and revoke cards | | `contacts:read` | Read contacts | | `contacts:write` | Create and update contacts | | `transfers:ach:write` | Initiate ACH transfers | | `transfers:book:write` | Initiate book transfers | | `transfers:crypto:write` | Initiate crypto transfers | | `transfers:international:write` | Initiate international transfers | | `transfers:usdc:write` | Initiate USDC transfers | | `transfers:wire:write` | Initiate wire transfers | | `webhooks:read` | Read webhook subscriptions and deliveries | | `webhooks:write` | Create, update, and delete webhook subscriptions | | `entity:create` | Create new business entities. Available on user-scoped keys only | | `simulations:write` | Trigger simulated events. Sandbox and development only | ## Multi-Entity Access For API keys with access to multiple entities, include the `x-entity-id` header to scope requests to a specific entity: ```bash theme={null} curl -H "x-api-key: YOUR_API_KEY" \ -H "x-entity-id: YOUR_ENTITY_ID" \ https://api.meow.com/v1/accounts ``` Use the `/api-keys/accessible-entities` endpoint to list all entities accessible by your API key. ## Environments ``` https://api.meow.com/v1 ``` Live environment for production integrations. ``` https://api.sandbox.meow.com/v1 ``` Test environment for development and testing. ``` https://api.dev.meow.com/v1 ``` Development environment for early integration testing. ## Security Best Practices * Never expose your API key in client-side code * Rotate keys regularly using the Meow Dashboard # Meow API changelog: new endpoints, fixes, and updates Source: https://developer.meow.com/changelog Stay current with new Meow API endpoints, breaking changes, improvements, and bug fixes shipped across the Customer, Billing, and Partner APIs. **Payments**: attach your own metadata for reconciliation * ACH, wire, book, crypto, international, and scheduled ACH create requests accept a `metadata` object with up to 20 string pairs and a 5 KB total limit. * Meow returns the metadata unchanged in create responses, approval reads, payment reads, and snapshot webhook events. A scheduled ACH payment inherits the schedule's metadata. * Use metadata to carry identifiers from your system without parsing a payment description. [Guide](/guides/transfers#attach-your-own-metadata). **Crypto transfers**: a refund in flight is told apart from a refund that landed * **Breaking**: `returned` now means only that the refund reached the sender. A transfer whose delivery failed and whose refund is still moving reports `refunding` and then `returned` when the money is back. Previously both stages reported `returned`, so a consumer could not tell an owed refund from a settled one. Reconcile on `returned`, and treat `refunding` as in-flight. * **Breaking**: a transfer the network could not deliver now reports `refunding` rather than `failed`, because a refund follows it. `failed` is now reserved for a transfer that ended with no refund in progress. A consumer that alerted on `failed` for undeliverable transfers should read `refunding` instead. * The `status` values are unchanged; what changed is which provider states map onto them. [Catalog](/api-reference/webhooks/events#crypto_transfer). * `GET /accounts/{account_id}/crypto/{transaction_id}` follows the same split: an in-flight refund reads `processing` and only a settled refund reads `returned`. * The inbound `id` is the deposit's own identifier, an unprefixed UUID. The event catalog example previously showed a prefixed form that the API does not emit. **Information requests**: explain a document you cannot provide, and read what is outstanding as data * `POST /entities/{entity_id}/info-requests/{request_id}` accepts `unavailable_proof_explanations`, keyed by `proof_type`. An accepted explanation settles that document exactly as an upload does, so a request for a document the business cannot produce no longer stays open forever. [Guide](/guides/onboarding#when-the-business-cannot-provide-a-document). * `answers` on that endpoint is now optional, so a request that asks no questions can be settled with explanations alone. * **Breaking**: `outstanding` changes from a list of strings to a list of objects with `kind`, `proof_type`, and `detail`, on both `EntityInfoRequestAnswersResponse` and the `info_request.created` / `info_request.updated` webhook payloads. `detail` carries the previous string unchanged, so matching on `kind` and `proof_type` replaces parsing prose. * `outstanding` is also reported per request on `next_step.info_requests[]`. [Reference](/api-reference/onboarding/submit-info-request-answers). * Outstanding documents now name the proof type in the same public vocabulary as `required_proof_types`. Previously an item could read "Uk Shareholder Registry" beside a `required_proof_types` of `shareholder_registry`. * `GET /entities/{entity_id}/info-requests/{request_id}/schema` now returns a `description` on each `file_upload` element, carrying the same explanation the Meow dashboard shows. [Reference](/api-reference/onboarding/get-info-request-schema). **Crypto transfers**: one bidirectional lifecycle webhook * `crypto_deposit.created` and `crypto_deposit.updated` are replaced by `crypto_transfer.created` and `crypto_transfer.updated`. This is a breaking rename with no compatibility period. Existing subscriptions are migrated to the new event names. Update consumers before deployment; queued legacy events are not translated to the new payload. * The `data.object` value is now `crypto_transfer`. `direction` identifies inbound deposits and outbound sends. `deposit_tx_hash` is renamed to `source_tx_hash`. * Wallet-funded and direct bank-funded outbound sends now emit lifecycle updates from Bridge provider state changes. [Catalog](/api-reference/webhooks/events#crypto_transfer). * `GET /accounts/{account_id}/crypto/{transaction_id}` now includes `status` for incoming and outgoing transactions. [Reference](/api-reference/core-api/get-crypto-transaction). * `GET /accounts/{account_id}/transactions?deposit_tx_hash=...` now resolves deposits into bank accounts as well as global accounts. * Crypto transfer events include `account_id`. Webhook subscriptions are entity-scoped, so every matching subscription for the entity receives the event. Use `account_id` to route it. **Bill Pay**: pay a vendor in stablecoin * `POST /bills` and `PATCH /bills/{bill_id}` accept `payment_type: "CRYPTO"` with a `crypto_contact_destination_id`, one of the vendor's saved crypto addresses from the contact's `payment_methods.crypto[].id`. The bill stays a draft for dashboard approval, as every bill created through the API does. A crypto bill must be in `USD`. [Reference](/api-reference/billpay/create-bill), [Guide](/guides/billpay#1-create-a-bill). * `GET /bills/{bill_id}` returns `bill_payment_details.crypto_destination`, the address a crypto bill will pay. * The same fields are available through the `create_bill` and `update_bill` MCP tools. **Transfers**: pay a contact in their own currency * `POST /accounts/{account_id}/international` sends a payment in the recipient's local currency, converting from US dollars at the rate quoted when you call. Fix either leg: pass `recipient_amount` to send an exact amount in their currency, or `source_amount` to spend an exact number of dollars. [Reference](/api-reference/core-api/create-international-payment), [Guide](/guides/transfers#international-payments). * `POST /accounts/{account_id}/international/quote` prices a payment without sending it, returning the `rate`, the `fee` and the `total_debit` so you can show them before committing. It reserves nothing, and the payment is priced again when you send, so figures can move with the rate. [Reference](/api-reference/core-api/quote-international-payment). * `GET /contacts/international-requirements` reports the fields a destination needs, given a currency and bank country. Destinations differ: some route on an IBAN, others on a domestic routing code, and some want the recipient's address. Check `supported` first, since not every currency reaches every country. [Reference](/api-reference/core-api/get-international-contact-requirements). * `POST /contacts` accepts `international` alongside `counterparty` and `crypto_address`, carrying the bank details those requirements ask for. Provide exactly one of the three. [Reference](/api-reference/core-api/create-contact). * To send US dollars instead, including to a bank outside the US, keep using `POST /accounts/{account_id}/wire`. **Accounts**: convert incoming crypto to USD automatically * `POST /accounts/{account_id}/liquidation-addresses` creates a blockchain address that converts cryptocurrency sent to it into USD and deposits it into the account you name. Repeating the same account, network and currency returns the address that already exists rather than creating another. [Reference](/api-reference/core-api/create-liquidation-address). **Onboarding**: see which agreements an application still owes * `GET /entities/{entity_id}` now returns `consent`, carrying `status`, `consented_items`, `remaining_items`, `viewed_at` and `completed_at`. Poll it to see whether the primary representative has finished the agreements behind `consent_url`. [Reference](/api-reference/onboarding/get-onboarding-status). * `next_step.step` reports `accept_terms` after you submit while agreements are outstanding, and lists the outstanding ones in `next_step.missing`. It moves on to `monitor_review` once they are all accepted. * Previously a submitted application reported `monitor_review` with nothing missing even when every agreement was still unaccepted, so there was no way to tell from the API that it was waiting on the representative. **Transfers**: send USDC over XDC * `BlockchainNetwork` accepts `XDC`, and `PaymentMethodType` accepts `USDC_XDC`. **Contacts**: a contact returns its address * `address` on a contact now returns the full address object rather than being omitted. **Transfers**: read incoming crypto deposits, and tell the two directions apart * `GET /accounts/{account_id}/crypto/{transaction_id}` now returns incoming deposits as well as outgoing transfers. An inbound deposit carries `source_address`, the on-chain address the funds came from, and `source_tx_hash`, the deposit's on-chain hash. Use them to match a deposit to whoever sent it. [Reference](/api-reference/core-api/get-crypto-transaction). * `source_address` is null when the sender is not known to us, so treat a null as "unattributed" rather than assuming an address is always present. * Pass the transaction ID that List Transactions reports. The same ID comes back on the response, so you can fetch the transfer again with it. [Reference](/api-reference/core-api/list-transactions-for-an-account). * Every transfer response now carries `direction`, either `inbound` or `outbound`. ACH and wire have returned both directions for a while, and the field says which one you are looking at instead of leaving you to infer it. [ACH](/api-reference/core-api/get-ach-transfer), [Wire](/api-reference/core-api/get-wire-transfer). * On an inbound wire, `counterparty_account_number`, `counterparty_routing_number` and `counterparty_bank_name` are now null when the bank did not report them, where they previously came back as empty strings. `purpose` is null inbound, since a purpose is something you state when sending. **Idempotency**: crypto and USDC transfers accept the key * `POST /accounts/{account_id}/crypto` and `POST /accounts/{account_id}/usdc` accept the `Idempotency-Key` header, so a retried send cannot create a second transfer. It is optional for now and will become required. [Reference](/api-reference/core-api/create-crypto-transaction). **Accounts**: correlate a new account with your own reference * `POST /accounts` accepts an optional `reference_id`, your own identifier of up to 255 characters. It comes back on the create response, on `GET /accounts` and `GET /accounts/{account_id}` as `referenceId`, and in the `account.created` / `account.updated` webhooks. [Reference](/api-reference/core-api/open-account). * This is how you match an account to the request that opened it. It matters most for products that open in the background: that response carries no `account_id`, so the reference is the only thing tying the later `account.created` event to your call. [Catalog](/api-reference/webhooks/events). * It is a label, not an idempotency key, so reuse it freely. Use the `Idempotency-Key` header to make a retry safe. **Idempotency**: send the key in the `Idempotency-Key` header * Every write that accepts an idempotency key now takes it in the `Idempotency-Key` header: 1-50 printable ASCII characters, no spaces. Reusing a key is rejected with a `400`. * `POST /accounts` **now requires** the header. Opening an additional account was previously unguarded, so a retried request could open a second account. [Reference](/api-reference/core-api/open-account). * Transfers and Bill Pay accept the header today; their `idempotency_key` **body field is deprecated** but still works. When you send both, they must match. [Transfers](/guides/transfers#book-transfer-tips), [Bill Pay](/guides/billpay#1-create-a-bill). * Webhook writes already required the header, and the guide's examples now show it. [Guide](/guides/webhooks#1-create-a-subscription). **Webhooks**: account lifecycle events * `account.created` fires when a Meow deposit account is live and addressable on `GET /accounts`. For Treasury Prime that is when the bank confirms the application, later than the `POST /accounts` response, so you can subscribe instead of polling. `account.updated` fires on status changes, including closes. [Catalog](/api-reference/webhooks/events). **Onboarding**: hosted consent, additional products, and non-US businesses * Submitting an application now returns a `consent_url`, a hosted page where the primary representative reviews and accepts the required agreements, disclosures, and attestations in their browser, no login needed. The same link is returned on `GET /entities/{entity_id}` after submission. The `attestation` request field is retired; it is ignored if you still send it. [Guide](/guides/onboarding#6-submit-the-application). * `product_features` on `POST /entities/{entity_id}/submit` requests additional products alongside the checking account. `stablecoins` requests USDC access, enabled once the representative accepts the terms and the application is approved. * `GET /entities/{entity_id}/representatives` lists every representative with their KYC status. [Reference](/api-reference/onboarding/list-representatives). * Non-US businesses can now complete onboarding through the API. [Guide](/guides/onboarding#international-businesses). **Webhooks**: global subscriptions. Subscribe once with a global API key and receive events for every entity your user administers, including entities added later. Each delivery carries `entity_id` for routing. [Reference](/api-reference/webhooks-global/create-subscription). **Accounts**: `PATCH /accounts/{account_id}` sets a custom account nickname (2-25 characters) shown in the dashboard. [Reference](/api-reference/core-api/update-account). **Webhooks**: delivery sequence numbers and partner webhooks * **Out-of-order protection**: every resource event now carries a per-resource `sequence`. Keep the highest you've applied per resource and drop anything at or below it. Monotonic, not gapless. [How it works](/guides/webhooks#5-handle-out-of-order-deliveries). * **Richer delivery history**: `GET /webhooks/deliveries` rows now include `event_type`, `resource_object`, `resource_id`, and `sequence`, so you can find and redrive a failure without opening the event. * **Partner Webhooks**: subscribe to onboarding `application.*` events with a partner API key. Same delivery machinery, scopes `partner:webhooks:read` / `partner:webhooks:write`. [Reference](/api-reference/partner-webhooks/create-subscription). * **Stricter allowlists**: `event_types` now rejects events a subscription can't receive: `webhook.test`, `message.attempt.exhausted`, and (on entity subscriptions) `application.*` return a 400. * Deleting a subscription retains its delivery history and frees the URL for reuse. **Webhooks**: subscribe to events and inspect deliveries * `POST /webhooks/subscriptions` to subscribe. Choose `payload_mode`: `snapshot` (full resource) or `thin` (`{id, object}`). * 13 events across ACH, wire, inbound transactions, check deposits, and global accounts. Dispatch on `data.status`. * Signed with [Standard Webhooks](https://www.standardwebhooks.com/) headers. * `GET /webhooks/deliveries` and `/attempts` for history; `POST /webhooks/deliveries/{id}/redrive` to replay one. * New scopes: `webhooks:read`, `webhooks:write`. * [Guide](/guides/webhooks) · [event catalog](/api-reference/webhooks/events). **Cards**: `PATCH /cards/{card_id}` to freeze, unfreeze, or update spend limits on a card **Tax Forms**: `GET /tax-forms` and `GET /tax-forms/{taxFormId}` to retrieve IRS 1099 tax forms **Instant payments**: FedNow supported as a payment rail **Partner Onboarding API**: Submit and track partner onboarding applications * `POST /partner-onboarding/applications` - Create an application * `GET /partner-onboarding/applications` - List applications * `GET /partner-onboarding/applications/{application_id}` - Get application details * `POST /partner-onboarding/applications/{application_id}/upload-url` - Get a presigned upload URL * `POST /partner-onboarding/applications/{application_id}/confirm-document` - Confirm a document upload **Approval Workflow**: Programmatic transfers now flow through configurable approval policies * Pending-approval state surfaced to API callers * Mutating endpoints honor entity-level approval requirements **Payment Networks**: `GET /accounts/{account_id}/payment-networks` now returns deposit instruction fields for each supported method **Audit Logging**: Structured audit logs added to all mutating Meow API endpoints **Wallet Transfers**: The Meow API now supports transfers from Global Account wallets * `crypto_id` accepted on wallet crypto transfers; currency is derived from the destination **Invoices**: Added lifecycle tracking fields (e.g., `paid_at`, `voided_at`, `sent_at`) to Invoice responses **MCP Server**: New MCP server for the Meow API with OAuth **Crypto Transaction API**: New endpoint for crypto transfers * `GET /accounts/{account_id}/crypto/{transaction_id}` - Get crypto transaction details * `POST /accounts/{account_id}/crypto` - Create crypto transaction (replaces legacy USDC endpoint) * New API scope: `transfers:crypto:write` **Check Deposit API**: New endpoint for check deposit details * `GET /accounts/{account_id}/check-deposits/{transaction_id}` - Get check deposit details **Deprecated** * `POST /accounts/{account_id}/usdc` - Use `POST /accounts/{account_id}/crypto` instead * `GET /accounts/{account_id}/transactions/{transaction_id}` - Use `GET /accounts/{account_id}/crypto/{transaction_id}` instead **Treasury Products**: Commercial paper and liquid treasuries holdings exposed in the Meow API **Transfer Lookup**: ACH and wire transfer GET endpoints now resolve Global Account-originated transfers * Clarified that wire and ACH transfer GET endpoints return details for both incoming and outgoing transfers * Updated Transfers tag description to include all transfer types **Billpay API**: Bill management endpoints * `GET /bills` - List all bills with optional status filtering and pagination * `GET /bills/{bill_id}` - Get bill details including line items * New API scope: `billpay:read` **Crypto Fees**: Removed `total_fee_percentage` from crypto transaction API responses (use the per-fee breakdown instead) **Scheduled ACH Transfers**: Create and manage scheduled ACH transfers * `POST /accounts/{account_id}/ach/scheduled` - Create a scheduled ACH transfer * `GET /accounts/{account_id}/ach/scheduled` - List scheduled ACH transfers * `transferMethod` query parameter on transaction list endpoint to filter by transfer method (e.g., INCOMING\_ACH, OUTGOING\_WIRE, CARD) **Improvement** * Updated `first_occurrence` field in recurring payment schemas to use `Timestamp` reference type **ACH Transfer Details**: Retrieve specific ACH transfer information * `GET /accounts/{account_id}/achs/{ach_transfer_id}` - Get ACH transfer details * `RETURNED` status added to `TransferStatus` enum * `CASH_SOLANA` added to `PaymentMethodType` enum * `internal_note` field added to wire transfer response **Contacts API**: Full CRUD operations for managing payment contacts * `POST /contacts` - Create a new contact with ACH/wire/check or crypto address details * `GET /contacts` - List all contacts with optional nickname filtering * `GET /contacts/{contact_id}` - Get contact details * `PATCH /contacts/{contact_id}` - Update contact information and add payment details * `DELETE /contacts/{contact_id}` - Delete a contact * New API scopes: `contacts:read` and `contacts:write` **Billing API**: Complete invoice management system * **Products**: `GET /billing/products`, `POST /billing/products`, `GET /billing/products/{product_id}` * **Customers**: `GET /billing/customers`, `POST /billing/customers`, `GET /billing/customers/{customer_id}` * **Invoices**: `GET /billing/invoices`, `POST /billing/invoices`, `GET /billing/invoices/{invoice_id}`, `GET /billing/invoices/{invoice_id}/pdf` * **Line Items**: `GET /billing/invoices/{invoice_id}/line-items`, `GET /billing/invoices/{invoice_id}/line-items/{line_item_id}` * **Payment Config**: `GET /billing/payment-method-types`, `GET /billing/accounts` * New API scopes: `billing:products:read/write`, `billing:customers:read/write`, `billing:invoices:read/write`, `billing:accounts:read` * Recurring invoice support with RFC 2445 RRULE scheduling * Invoice email notifications with custom recipient lists **Transfer APIs**: Comprehensive transfer capabilities * `POST /accounts/{account_id}/book` - Create internal book transfers between accounts * `POST /accounts/{account_id}/ach` - Create ACH transfers to external counterparties * `POST /accounts/{account_id}/wire` - Create wire transfers to external counterparties * `GET /accounts/{account_id}/wires/{wire_transfer_id}` - Get wire transfer details * New API scopes: `transfers:book:write`, `transfers:ach:write`, `transfers:wire:write` **Payment Networks API**: * `GET /accounts/{account_id}/payment-networks` - List payment networks supported by an account **Routing Number Validation**: * `GET /routing-numbers/{routing_number}` - Validate routing numbers and get bank information **API Keys Management**: * `GET /api-keys/accessible-entities` - List all entities accessible by the current API key * `GET /api-keys/current` - Get metadata about the current API key **Multi-entity Support**: * Optional `x-entity-id` header to scope API requests to specific entities Combined Read and Write APIs into a single unified Meow API. **Accounts API**: Comprehensive financial data access * `GET /accounts` - List bank accounts * `GET /accounts/{account_id}` - Get account details * `GET /accounts/{account_id}/transactions` - List transactions with time filtering and pagination * `GET /accounts/{account_id}/balances` - List account balances with time filtering * New API scopes: `accounts:read`, `accounts:transactions`, `accounts:balances` **USDC Transaction Support**: * `POST /accounts/{account_id}/usdc` - Create USDC transactions * `GET /accounts/{account_id}/transactions/{transaction_id}` - Get USDC transaction details * New API scope: `transfers:usdc:write` * Support for multiple blockchain networks: Ethereum, Solana, Base **Health Check**: * `GET /health` - API service health status endpoint **Authentication & Security**: * API key authentication via `x-api-key` header * Standardized error handling with detailed validation responses * Scoped API permissions for granular access control # Meow app changelog: dashboard updates and new features Source: https://developer.meow.com/changelog/app Track the latest releases for the Meow dashboard and product, including payments, cards, bills, invoicing, multi-currency, and onboarding updates. * **Navigation**: Sidebar utility links consolidated into the user menu * **Bills**: Multi-currency (FX) bills now support submission and beneficiary display **Payments & transfers** * Real-time payments: RTP and FedNow instant payment support * USDC on the Sui network * Bulk payments: live status page with real-time progress and routing-number checksum validation * Stablecoin reimbursement payouts **Cards** * Filter and search on the cards pages * Card status change history and richer transaction details **Bills & invoicing** * Invoice OCR now recognizes prior payments * Additional documents can be attached to bills * Accounting mappings available for pending bills with inline sync-status * SWIFT payin details and invoice custom fields shown on public payment links **Multi-currency** * USD card visible in the multi-currency dashboard **Settings & security** * New settings sidebar: account, profile, security, notifications, and fees * IP allowlist editing for company and user API keys * Resend invitations from the workspace **Onboarding** * Germany (DE) added as a supported country **Dashboard refresh** * Refreshed accounts sidebar and primary color * New navigation, step-up authentication on sensitive actions, and a dedicated stablecoins area **Stablecoins & crypto** * USDT on Solana * Stablecoin transfer and transaction panels * Crypto in unified bulk payments **Cards & banking** * Card issuing offered during the invitation flow * ACH return reasons surfaced in transaction details * Settings now groups currencies with shared fees **Receipts & bills** * More accurate receipt parsing from `receipts@meow.com` * Bills searchable by vendor name **Onboarding & compliance** * Australian entities supported * Information request banner and settings page; submit follow-up answers in parts **Reimbursements** * Approval flow with step-up authentication * DRAFT reimbursement status **Developer & API** * API documentation moved to `developer.meow.com` * Renew API key button and toggleable email notifications on key creation **Reimbursements** * Spend categories and per-user limits * Filtered CSV export * Step-up authentication on submission **International & global accounts** * Set the sender name shown on outgoing wires **Banking** * Monthly PDF statements for the Global Account * Global Account verification letter PDF download * Card frozen alerts for suspicious transaction activity **Bills & cards** * Crypto bill payment emails with tx hash and destination links * "Make bill recurring" action on the bill table dropdown **Transactions** * Estimated arrival date displayed in transaction details **International payments** * International wires (USD) from Global Accounts with outgoing transfers view **Global accounts** * New Send Money flow for the Global Accounts page * Bulk payments from the Global Account * Recipient completion email * Recently paid vendors in Send Money **Crypto & stablecoins** * USDG (Global Dollar) cryptocurrency support * Full blockchain address shown in crypto deposit screens * Blockchain explorer link in crypto bill payment emails **Reimbursements** * Email notification toggle and bank selection grid in setup * Dropdown and drag-and-drop upload flow **Cards** * "Block this merchant" action on card transaction detail * Spend insights on cards **Bill pay** * @mentions in bill comments * Success toast notifications and payment-method icons in the bill pay table **Onboarding & verification** * Ireland address support for business verification * Downloadable Banking Authorization Letter template # Meow MCP changelog: new AI agent capabilities Source: https://developer.meow.com/changelog/mcp Track new capabilities for the Meow MCP server, including agent-led signup, card management, document uploads, and improved error handling. **Sign up by chatting with an agent** * Your agent can walk you through opening a Meow account end-to-end * Add, update, or remove owners and signers without leaving the chat * Upload required documents through the agent **Cards** * Your agent can freeze cards, edit spend limits, and review card activity **When something goes wrong** * Clearer error messages * Every response includes a request ID you can share with support **Connect Claude, Cursor, or any AI assistant to your Meow account** * Sign in once with OAuth, with no API keys to copy around # Meow API error codes, response format, and debugging Source: https://developer.meow.com/errors Understand the JSON error response format and reference the common Meow API error codes for accounts, transactions, contacts, and authentication failures. Errors return JSON with `code`, `message`, and `debug_message`. The `code` aligns with FDX where possible; `debug_message` is for troubleshooting only. ## Error Response Format ```json theme={null} { "code": 701, "message": "Account not found", "debug_message": "Account not found. Use /accounts to get a list of accounts and their IDs." } ``` ## Common Error Codes | Code | Description | | ----- | --------------------------------------------- | | `500` | Internal server error | | `501` | Subsystem unavailable | | `503` | Scheduled maintenance | | `601` | Data not found | | `602` | Customer not authorized | | `701` | Account not found | | `703` | Invalid input (including invalid date ranges) | | `704` | Account type not supported | | `705` | Account is closed | | `801` | Transaction not found | # Quickstart: make your first Meow API call in minutes Source: https://developer.meow.com/getting-started Generate an API key, send your first authenticated request, and explore Meow sandbox and production environments for testing and live integrations. ## Prerequisites * A Meow account * An API key from the [Meow Dashboard](https://www.meow.com/dashboard) ## Make your first API call Authenticate by passing your API key in the `x-api-key` header: ```bash theme={null} curl -H "x-api-key: YOUR_API_KEY" https://api.meow.com/v1/accounts ``` A successful response returns your accounts. ## Environments | Environment | Base URL | | ----------- | --------------------------------- | | Production | `https://api.meow.com/v1` | | Sandbox | `https://api.sandbox.meow.com/v1` | | Development | `https://api.dev.meow.com/v1` | Use the sandbox environment for development and testing before going live. ## What can you build? Access account information, transactions, balances, and payment networks. Send ACH, wire, book, and crypto transfers programmatically. Manage cryptocurrency contacts for USDC transactions. Create invoices with line items, discounts, and flexible payment options. ## Next steps Learn about [API key scopes and multi-entity access](/authentication). Try [sending a USDC transfer](/guides/transfers) or [creating an invoice](/guides/invoicing). Browse the full [API Reference](/api-reference/overview) for all available endpoints. # Create and track vendor bills via the API Source: https://developer.meow.com/guides/billpay Draft bills payable to your vendors, attach invoices, track them through payment, and schedule recurring bills, all as drafts for dashboard approval. Bill Pay is Meow's accounts-payable surface. You record what you owe a vendor as a **bill**, including its line items, due date, and how it should be paid, and the API saves it as a draft. Someone on your team approves the bill in the Meow dashboard, and only then does it get paid. Every bill you touch through the API stays a **draft pending approval**. Creating a bill, editing it, or attaching documents never moves money on its own. Payment happens only after a human approves the bill in the dashboard. ## You'll need * An API key with `billpay:read` to list and retrieve bills, and `billpay:write` to create, update, delete, and attach documents to them. * A **contact** for each vendor, created with banking (`ACH`, wire, or check) `counterparty` details, or with a `crypto_address` to pay the vendor in stablecoin. A bill is payable to a contact that must already exist; create one first with [`POST /contacts`](/api-reference/core-api/create-contact). (The [Contacts guide](/guides/contacts) covers crypto contacts.) * If your key can access more than one entity, pass the `x-entity-id` header to choose which entity the bill belongs to. ## 1. Create a bill A bill is payable to a `contact_id` and is made of one or more line items. Each line item is either an `EXPENSE` (a flat `amount`) or an `ITEM` (a unit `amount` times a `quantity`); the bill total is calculated from the lines. Send a unique `Idempotency-Key` header so a retried request never creates a duplicate. ```bash theme={null} curl -X POST https://api.meow.com/v1/bills \ -H "x-api-key: $MEOW_API_KEY" \ -H "Idempotency-Key: bill-2026-06-30-catnip-001" \ -H "Content-Type: application/json" \ -d '{ "contact_id": "b3f1c2d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", "invoice_number": "CATNIP-4471", "bill_date": "2026-06-30", "bill_due_date": "2026-07-30", "description": "June cloud hosting", "memo": "Approved by ops", "payment_type": "ACH", "currency": "USD", "line_items": [ { "type": "EXPENSE", "amount": 1200.00, "description": "Cloud hosting - June" }, { "type": "ITEM", "amount": 15.00, "quantity": 20, "description": "Extra storage (GB)" } ] }' ``` ```json Response theme={null} { "id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d", "status": "DRAFT", "vendor_name": "Catnip Cloud", "invoice_number": "CATNIP-4471", "description": "June cloud hosting", "bill_date": "2026-06-30", "bill_due_date": "2026-07-30", "bill_total": "1500.00", "currency": "USD", "payment_type": "ACH", "contact_id": "b3f1c2d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d", "created_at": "2026-06-30T10:30:00.000Z", "line_items": [ { "id": "1a2b3c4d-...", "type": "EXPENSE", "amount": "1200.00", "quantity": null, "description": "Cloud hosting - June", "amount_total": "1200.00" }, { "id": "2b3c4d5e-...", "type": "ITEM", "amount": "15.00", "quantity": "20", "description": "Extra storage (GB)", "amount_total": "300.00" } ] } ``` Save `id`. It is the `{bill_id}` for attaching documents, tracking, updating, or canceling the bill below. | Field | Description | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `idempotency_key` | **Deprecated.** Send the `Idempotency-Key` header instead. Still accepted for now; when both are sent they must match. | | `contact_id` | The vendor this bill is payable to. The vendor must already exist as a contact. | | `payment_type` | How the bill is paid once approved: `ACH` (default), `WIRE`, `CHECK`, or `CRYPTO`. | | `crypto_contact_destination_id` | Required when `payment_type` is `CRYPTO`: which of the vendor's saved crypto addresses to pay, taken from the contact's `payment_methods.crypto[].id`. Rejected for any other payment type. | | `account_id` | The account to pay from, in the id format returned by `GET /accounts`. Defaults to your configured bill pay account. | | `currency` | The bill's currency (ISO 4217). Defaults to `USD`. | | `bill_date` / `bill_due_date` | The date on the bill and the date it's due (`YYYY-MM-DD`). | | `line_items` | The lines that make up the bill. Use `type: "ITEM"` with a `quantity` for itemized lines, or `type: "EXPENSE"` for a flat amount. | | `rrule` | An iCalendar RRULE to make this a [recurring bill](#5-schedule-recurring-bills). | Bill Pay accepts the `ACH`, `WIRE`, `CHECK`, and `CRYPTO` payment types. A `CRYPTO` bill must be in `USD` and names one of the vendor's saved crypto addresses; the bill's `bill_payment_details.crypto_destination` reports the address it will pay. International payments are arranged in the Meow dashboard, not through this API. ## 2. Attach supporting documents Attaching the vendor's invoice or a receipt is a three-step flow: ask for upload URLs, upload each file, then confirm the attachment. You can attach up to five documents per confirm call. Describe each file, giving its `filename`, `content_type`, and `size` in bytes, and get a short-lived upload target for each. ```bash theme={null} curl -X POST https://api.meow.com/v1/bills/$BILL_ID/documents/presigned-urls \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "files": [ { "filename": "catnip-cloud-4471.pdf", "content_type": "application/pdf", "size": 48213 } ] }' ``` ```json Response theme={null} { "uploads": [ { "url": "https://uploads.meow.com/billpay", "fields": { "key": "billpay/9a8b.../catnip-cloud-4471.pdf", "policy": "eyJ...", "x-amz-signature": "abc123" }, "object_key": "billpay/9a8b.../catnip-cloud-4471.pdf", "filename": "catnip-cloud-4471.pdf", "max_bytes": 10485760 } ] } ``` POST the file to its `url` as a multipart form, including every entry from `fields` and putting the file last. Keep it under `max_bytes` and upload before the URL expires. ```bash theme={null} curl -X POST "https://uploads.meow.com/billpay" \ -F "key=billpay/9a8b.../catnip-cloud-4471.pdf" \ -F "policy=eyJ..." \ -F "x-amz-signature=abc123" \ -F "file=@catnip-cloud-4471.pdf" ``` Confirm each upload with the `object_key` and `filename` you received. The response is the updated bill, now carrying the document under `additional_documents` with a short-lived download URL. ```bash theme={null} curl -X POST https://api.meow.com/v1/bills/$BILL_ID/documents \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "documents": [ { "object_key": "billpay/9a8b.../catnip-cloud-4471.pdf", "filename": "catnip-cloud-4471.pdf" } ] }' ``` ```json Response theme={null} { "id": "9a8b7c6d-...", "status": "DRAFT", "vendor_name": "Catnip Cloud", "additional_documents": [ { "id": "d1e2f3a4-...", "name": "catnip-cloud-4471.pdf", "url": "https://download.meow.com/...", "created_at": "2026-06-30T10:32:00.000Z" } ] } ``` ## 3. Track bills `GET /bills` returns a paginated list and filters by status, vendor, amount, and date ranges, plus full-text `search`. ```bash theme={null} curl "https://api.meow.com/v1/bills?statuses=DRAFT&statuses=PENDING&due_date_to=2026-07-31&limit=25" \ -H "x-api-key: $MEOW_API_KEY" ``` | Parameter | Description | | --------------------------------- | ----------------------------------------------------------------------- | | `statuses` | Filter by one or more bill statuses; repeat the parameter for several. | | `contact_id` | Filter to bills payable to specific vendors. | | `amount_min` / `amount_max` | Filter by bill total. | | `bill_date_from` / `bill_date_to` | Filter by the date on the bill. | | `due_date_from` / `due_date_to` | Filter by due date. | | `search` | Full-text search across bill fields. | | `sort_by` / `sort_descending` | Order the results. | | `limit` / `offset` | Page through results using the `nextOffset` from the previous response. | Retrieve one bill with `GET /bills/{bill_id}`: ```bash theme={null} curl https://api.meow.com/v1/bills/$BILL_ID -H "x-api-key: $MEOW_API_KEY" ``` A bill moves through these statuses as it's approved and paid: | `status` | Meaning | | ------------------- | ------------------------------------------------------ | | `DRAFT` | Created and waiting for approval in the dashboard. | | `PENDING` | Submitted and moving through approval. | | `APPROVED` | Approved and ready to be scheduled. | | `SCHEDULED` | Approved with a payment scheduled; see `scheduled_at`. | | `PAYMENT_INITIATED` | Payment is on its way. | | `PAYMENT_DELIVERED` | Payment delivered; see `paid_at`. | | `PAYMENT_FAILED` | The payment attempt failed. | | `REJECTED` | An approver declined the bill. | | `CANCELED` | The bill was canceled before payment. | ## 4. Update or cancel a draft While a bill is still a `DRAFT`, you can edit it. `PATCH` uses partial semantics: only the fields you send change, and sending `line_items` replaces the existing lines (send an empty list to clear them). ```bash theme={null} curl -X PATCH https://api.meow.com/v1/bills/$BILL_ID \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "memo": "Revised per updated invoice", "payment_type": "WIRE" }' ``` Cancel a bill that hasn't been approved yet by deleting it: ```bash theme={null} curl -X DELETE https://api.meow.com/v1/bills/$BILL_ID -H "x-api-key: $MEOW_API_KEY" ``` Once a bill is approved (or paid), it can no longer be edited or deleted through the API. ## 5. Schedule recurring bills To generate a bill on a repeating schedule, include an iCalendar `rrule` (with a `DTSTART`) when you create it. Each occurrence is generated as its own draft for approval. Recurring bills are never paid automatically. ```bash theme={null} curl -X POST https://api.meow.com/v1/bills \ -H "x-api-key: $MEOW_API_KEY" \ -H "Idempotency-Key: bill-rent-recurring-001" \ -H "Content-Type: application/json" \ -d '{ "contact_id": "c4d5e6f7-8a9b-0c1d-2e3f-4a5b6c7d8e9f", "description": "Monthly office rent", "payment_type": "ACH", "rrule": "DTSTART:20260701T000000Z\nRRULE:FREQ=MONTHLY;BYMONTHDAY=1", "line_items": [ { "type": "EXPENSE", "amount": 4500.00, "description": "Office rent, Whisker Works" } ] }' ``` Building the schedule string by hand is fiddly. A tool like the [RRULE generator](https://icalendar.org/rrule-tool.html) will assemble the `DTSTART`/`RRULE` for you. ## End to end ```bash theme={null} # 1. Create the bill and capture its id. BILL_ID=$(curl -s -X POST https://api.meow.com/v1/bills \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -H "Idempotency-Key: bill-$(date +%Y%m%d)-catnip-001" \ -d '{"contact_id":"b3f1c2d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d","invoice_number":"CATNIP-4471","payment_type":"ACH","line_items":[{"type":"EXPENSE","amount":1200.00,"description":"Cloud hosting - June"}]}' \ | jq -r .id) # 2. Request an upload URL for the vendor's invoice PDF. UPLOAD=$(curl -s -X POST https://api.meow.com/v1/bills/$BILL_ID/documents/presigned-urls \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d '{"files":[{"filename":"catnip-cloud-4471.pdf","content_type":"application/pdf","size":48213}]}') OBJECT_KEY=$(echo "$UPLOAD" | jq -r '.uploads[0].object_key') # 3. Upload the file to .uploads[0].url with .uploads[0].fields, then confirm. curl -s -X POST https://api.meow.com/v1/bills/$BILL_ID/documents \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d "{\"documents\":[{\"object_key\":\"$OBJECT_KEY\",\"filename\":\"catnip-cloud-4471.pdf\"}]}" | jq '.status' ``` The bill now sits as a `DRAFT` with its invoice attached, waiting for an approver in the dashboard. ## Bulk loads There is no batch endpoint. To load a run of bills — a month of contractor payments, for example — call `POST /bills` once per bill. Two things make a large run safe to retry: * **Give every bill its own `Idempotency-Key`, derived from your own record** (a payroll row id, an invoice number). Replaying the whole run then re-sends the same key per bill, so nothing is duplicated. * **Create the vendor contacts first.** A bill needs a contact that already carries the banking details for its payment type, so resolve or create contacts before the run rather than inside it. Drafting is capped at **500 bills per rolling 24 hours per creator** — your entity key, or the user a user-scoped key acts as. Creates past the cap return `429 Too Many Requests`; retry after the oldest drafts in the window age out, or split the run across keys. Every bill still lands as a `DRAFT` awaiting approval in the dashboard, so a bulk load never moves money on its own. ## See also Register a vendor with banking `counterparty` details to pay bills to. Full request contract for `POST /bills`. Every filter, sort, and pagination option. Edit a draft with `PATCH` partial semantics. Attach uploaded invoices and receipts. # Retrieve a card number with the Meow API Source: https://developer.meow.com/guides/card-numbers Step-by-step guide to securely retrieving the full card number, CVC, and expiration date for a virtual card using a single-use reveal URL. This guide explains how to retrieve the full card number for a card you created with the API. It covers: * Claiming the card number and receiving a reveal grant * Fetching the card number from the reveal URL * Why the request must go to the reveal URL and not the API origin ## Prerequisites * A card created through the API (see [Create Card](/api-reference/core-api/create-card)) * API key permissions: `cards:write` * The card must be virtual, active, and not expired Card number retrieval is enabled per account. Contact support to turn it on. Until it is enabled, both endpoints below return `403`. ## How it works The card number is never returned by the endpoint you call. Instead: 1. You claim the card number and receive a `reveal_url` plus a short-lived `reveal_token`. 2. You call that URL with the token to receive the card number, CVC, and expiration. Meow stores tokenized placeholders rather than card numbers. The real values are substituted into the response by our PCI vault as it passes through `vault.meow.com`, so the card number is never written to our databases, logs, or backups. ## Claim the card number Call the PAN endpoint with the `cardId` returned by Create Card: ```bash theme={null} curl -X POST "https://api.meow.com/v1/cards/{card_id}/pan" \ -H "x-api-key: YOUR_ENTITY_API_KEY" ``` The response contains the reveal grant, not the card: ```json theme={null} { "reveal_url": "https://vault.meow.com/v1/cards/515024f9-6ad2-4ce5-bc2f-b0820bf9336b/pan/reveal", "reveal_token": "eyJjYXJkX2lkIjoiNTE1MDI0ZjkuLi4ifQ.0a-KPzCD0gniw2n8Dk4gcq1hOYw", "token_expires_in_seconds": 300, "exp_month": 5, "exp_year": 2029 } ``` This can be called only once per card. Subsequent calls return an error, and that holds even if the call failed after the claim was recorded. If you lose the grant, revoke the card and create a new one. ## Fetch the card number Call the `reveal_url` exactly as returned, passing the `reveal_token` as a bearer token alongside your API key: ```bash theme={null} curl -X GET "https://vault.meow.com/v1/cards/{card_id}/pan/reveal" \ -H "x-api-key: YOUR_ENTITY_API_KEY" \ -H "Authorization: Bearer YOUR_REVEAL_TOKEN" ``` ```json theme={null} { "card_number": "4000009990002514", "cvc": "123", "exp_month": 5, "exp_year": 2029 } ``` The token is valid for 300 seconds. Request the card number at the point you need it rather than ahead of time. ## Use the reveal URL, not the API origin Sending the same request to `api.meow.com` instead of `vault.meow.com` returns `200 OK` with the same field names, but the values are tokenized placeholders: ```json api.meow.com theme={null} { "card_number": "tok_sandbox_azfe372ZXukbttABio4xCc", "cvc": "tok_sandbox_9MaYiyxaPDkzDAr3fvtpEz", "exp_month": 5, "exp_year": 2029 } ``` ```json vault.meow.com theme={null} { "card_number": "4000009990002514", "cvc": "123", "exp_month": 5, "exp_year": 2029 } ``` Meow returns the same response in both cases. The placeholders are what our API emits, and the card number is substituted into the response by the vault as it passes through `vault.meow.com`. Nothing about the request changes what we send. Both responses are `200 OK`, so a failed integration looks like a working one. If your code receives a value beginning with `tok_`, it called the wrong host. Read `reveal_url` from the response rather than constructing it. The host differs between sandbox and production, and hardcoding it is the most common cause of receiving placeholders. ## Eligibility A card can return its number only if all of the following are true: * It was created through the API. Cards created in the dashboard cannot return a number. * It is virtual, not physical. * It is not closed, suspended, or expired. These are checked again when you call the reveal URL, not only when you claim the number, so a card revoked within the five-minute window will not reveal. ## Handling errors The card number was already claimed. Revoke the card and create a new one. The card was created in the dashboard. Create the card through [Create Card](/api-reference/core-api/create-card) instead. The token is older than 300 seconds, belongs to a different card, or was altered. Claim a new card and retry, requesting the number closer to the point of use. The request went to the API origin instead of the reveal URL. Use the `reveal_url` value from the claim response verbatim. ## Next steps Create the virtual card you will retrieve a number for. Revoke a card whose number was claimed and is no longer needed. List authorizations and settled transactions for a card. API key types, scopes, and the `x-entity-id` header. # Issue and manage cards with the Meow API Source: https://developer.meow.com/guides/cards Step-by-step guide to issuing virtual cards, setting spend limits and merchant restrictions, freezing and revoking cards, and reviewing card transactions. This guide explains how to issue and manage cards with the Meow API. It covers: * Creating a virtual card with spend limits * Restricting a card to specific merchants or categories * Freezing, updating, and revoking a card * Reviewing transactions and spend ## Prerequisites * A Meow account with at least one enabled bank account * API key permissions: * Reading cards and transactions: `cards:read` * Creating and modifying cards: `cards:write` * Sandbox simulations: `simulations:write` ## Authentication Overview The headers you send depend on the type of API key: * **Entity API keys** scope all requests to a single entity. No `x-entity-id` header is required. * **Global API keys** can access multiple entities, and every request must include an `x-entity-id` header identifying the entity. Use `GET /v1/api-keys/accessible-entities` to discover which entities you can access. ## Card lifecycle A card moves through a small number of states, reported as `status` on the card: | Status | Meaning | | ----------- | ------------------------------------------------------------------------- | | `pending` | The card is being issued. Card details are not available yet. | | `active` | The card can authorize transactions. | | `suspended` | The card is frozen. Authorizations are declined, and you can unfreeze it. | | `closed` | The card is revoked. This is permanent. | | `failed` | Issuing did not complete. | ## Create a card `spending_controls` and `nickname` are required. Within `spending_controls`, only `per_transaction_limit` is required. All limits are in whole dollars. ```bash theme={null} curl -X POST "https://api.meow.com/v1/cards" \ -H "x-api-key: YOUR_ENTITY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "nickname": "Ads spend", "spending_controls": { "per_transaction_limit": 500, "monthly_limit": 5000 } }' ``` ```json theme={null} { "payment_type": "card", "controls": { "spending_controls": { "per_transaction_limit": 500, "monthly_limit": 5000 }, "single_use": false }, "metadata": { "card_id": "515024f9-6ad2-4ce5-bc2f-b0820bf9336b" } } ``` Record the `card_id`. Every other card operation uses it. ### Spend limits Each limit resets on its own schedule. Combine them to cap both a single purchase and a period of spend. | Field | Resets | | ----------------------- | --------------------------------------------------- | | `per_transaction_limit` | Not a running total. Applies to each authorization. | | `daily_limit` | Midnight UTC | | `weekly_limit` | Sunday at midnight UTC | | `monthly_limit` | The 1st at midnight UTC | | `yearly_limit` | January 1st at midnight UTC | | `all_time_limit` | Never | Limits are a ceiling on the card, not a reservation of funds. The combined limits across your cards can exceed your account balance, and each authorization is checked against your available balance at the moment it happens. A card within its own limit is still declined if the account cannot cover the transaction. ### Optional fields * `single_use` — revoke the card automatically after its first authorization. * `expires_at` — the card stops working at this time. It is not revoked automatically, so revoke it explicitly when you are done. * `debit_account` — the account the card draws from, as a `cash_account_` id from [List Accounts](/api-reference/core-api/list-bank-accounts). Omit to use the default. * `purpose` — a memo for your own records, recorded on the card's audit trail. It is never shown to the merchant. ## Restrict where a card can be used ### By merchant Fetch merchant IDs first, then reference them in `spending_restriction`. Use `allow` to limit the card to those merchants, or `block` to bar them. ```bash theme={null} curl -X GET "https://api.meow.com/v1/cards/merchants" \ -H "x-api-key: YOUR_ENTITY_API_KEY" ``` ```bash theme={null} curl -X POST "https://api.meow.com/v1/cards" \ -H "x-api-key: YOUR_ENTITY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "nickname": "Cloud hosting", "spending_controls": { "per_transaction_limit": 2000 }, "spending_restriction": { "type": "allow", "merchants": ["9f2c1e84-5b7a-4d63-9c10-2e8f4a6b7d31"] } }' ``` With `type` set to `allow`, the card authorizes only at those merchants. With `block`, it is refused at them and allowed everywhere else. `merchants` takes the merchant `id` values from `GET /v1/cards/merchants`, which are UUIDs. Merchant names will not match. You can list between 1 and 100 merchants. ### By category `allowed_categories` limits the card to a set of merchant categories, such as `Software`, `Meals`, or `Advertising`. Omit it to allow every category. ```bash theme={null} curl -X POST "https://api.meow.com/v1/cards" \ -H "x-api-key: YOUR_ENTITY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "nickname": "Team meals", "spending_controls": { "per_transaction_limit": 150, "weekly_limit": 1000 }, "allowed_categories": ["Meals", "Restaurants"] }' ``` ## Retrieve the card number Card numbers are returned through a single-use reveal URL rather than by the endpoint you call. See the [Card Numbers guide](/guides/card-numbers) for the full flow. ## Update a card `PATCH /v1/cards/{card_id}` changes one or more fields. Everything is optional, so send only what you want to change. **Freeze a card** by setting `status` to `inactive`, and unfreeze it with `active`: ```bash theme={null} curl -X PATCH "https://api.meow.com/v1/cards/{card_id}" \ -H "x-api-key: YOUR_ENTITY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "status": "inactive" }' ``` **Change limits or restrictions.** `spending_controls` and `spending_restriction` are replaced, not merged, so send the complete object you want: ```bash theme={null} curl -X PATCH "https://api.meow.com/v1/cards/{card_id}" \ -H "x-api-key: YOUR_ENTITY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "nickname": "Ads spend (Q4)", "spending_controls": { "per_transaction_limit": 800, "monthly_limit": 12000 } }' ``` Pass `null` to `spending_restriction` or `allowed_categories` to clear the restriction and allow any merchant or category. ## Revoke a card Revoking is permanent. The card moves to `closed` and cannot authorize again. ```bash theme={null} curl -X POST "https://api.meow.com/v1/cards/{card_id}/revoke" \ -H "x-api-key: YOUR_ENTITY_API_KEY" ``` Freeze the card instead if you may want to use it again. ## Review cards and spend **List cards**, with optional filters and pagination. Use the `nextOffset` from the response to page: ```bash theme={null} curl -X GET "https://api.meow.com/v1/cards?limit=25" \ -H "x-api-key: YOUR_ENTITY_API_KEY" ``` **List transactions** across cards: ```bash theme={null} curl -X GET "https://api.meow.com/v1/cards/transactions?limit=25" \ -H "x-api-key: YOUR_ENTITY_API_KEY" ``` Other read endpoints: * [Get Card](/api-reference/core-api/get-card) — a single card's status, last four, and restrictions. * [Get Card Limits](/api-reference/core-api/get-card-limits) — limits alongside spend against them. * [Get Card Details](/api-reference/core-api/get-card-details) — billing address, debit account, and for physical cards a tracking URL. * [Card Spending Insights](/api-reference/core-api/get-card-spending-insights) — spend grouped by merchant. ## Test in sandbox Cards do not need a real merchant to exercise. In sandbox, simulate the authorization lifecycle with the `simulations:write` scope: `POST /v1/simulations/card_authorizations` creates a pending authorization against a card. `POST /v1/simulations/card_authorizations/{authorization_id}/capture` settles it into a transaction. `.../increment` raises the authorized amount. `.../reverse` releases it, as a merchant does when canceling an order. `POST /v1/simulations/card_transactions/{transaction_id}/refund` refunds a settled transaction. This is the fastest way to confirm your limits, restrictions, and webhook handling behave the way you expect before going live. ## Next steps Retrieve the full card number, CVC, and expiration. Receive card and transaction events as they happen. Full request and response reference. API key types, scopes, and the `x-entity-id` header. # Manage USDC cryptocurrency contacts via the Meow API Source: https://developer.meow.com/guides/contacts Create, list, retrieve, update, and delete cryptocurrency contacts to send USDC across Ethereum, Solana, and Base networks with the Meow API. ## Prerequisites * A Meow account with contacts management capabilities * An API key with the `contacts:read` and `contacts:write` permissions ## Understanding Cryptocurrency Contacts Cryptocurrency contacts store blockchain addresses that you frequently send USDC to. Each contact includes: * A friendly nickname for easy identification * A cryptocurrency address for a specific blockchain network * A static memo ID used when creating transactions ## Creating Cryptocurrency Contacts ### Step 1: Prepare Contact Information Before creating a contact, gather the following information: * A nickname for the contact * The cryptocurrency address * The blockchain network the address belongs to (Ethereum, Solana, Base) Ensure the cryptocurrency address is valid for the selected blockchain network. ### Step 2: Create the Contact Use the following API call to create a new USDC contact: ```bash theme={null} curl -X POST "https://api.meow.com/v1/contacts/usdc" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "nickname": "Lion", "crypto_address": { "address": "0x1234567890abcdef1234567890abcdef12345678", "payment_rail": "ethereum" } }' ``` Replace: * `Lion` with your preferred nickname * The address with the actual cryptocurrency address * `payment_rail` with either `ethereum`, `solana`, or `base` depending on the blockchain network ### Step 3: Review the Response A successful response will include details about the created contact: ```json theme={null} { "id": "cont_987654321", "nickname": "Lion", "static_memos": { "ethereum": { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "destination_address": "0x1234567890abcdef1234567890abcdef12345678", "destination_payment_rail": "ethereum" } } } ``` Note the `static_memos` object, which contains the `id` you'll need when creating USDC transactions. Each blockchain network the contact supports will have its own entry in the `static_memos` object. ## Listing Cryptocurrency Contacts To view all your existing contacts, use the following API call: ```bash theme={null} curl -X GET "https://api.meow.com/v1/contacts" \ -H "x-api-key: YOUR_API_KEY" ``` You can optionally filter contacts by nickname using the `nickname_query` parameter: ```bash theme={null} curl -X GET "https://api.meow.com/v1/contacts?nickname_query=Lion" \ -H "x-api-key: YOUR_API_KEY" ``` Example response: ```json theme={null} { "data": [ { "id": "cont_987654321", "nickname": "Lion", "static_memos": { "ethereum": { "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "destination_address": "0x1234567890abcdef1234567890abcdef12345678", "destination_payment_rail": "ethereum" } } }, { "id": "cont_123456789", "nickname": "Lion 2", "static_memos": { "solana": { "id": "7bc85f64-5717-4562-b3fc-2c963f66abc8", "destination_address": "8xDc123456789abcdef123456789abcdef123456789abcdef", "destination_payment_rail": "solana" } } } ] } ``` ## Deleting Cryptocurrency Contacts If you no longer need a contact, you can delete it using the following API call: ```bash theme={null} curl -X DELETE "https://api.meow.com/v1/contacts/{contact_id}" \ -H "x-api-key: YOUR_API_KEY" ``` Replace `{contact_id}` with the ID of the contact you want to delete. A successful deletion will return a `204 No Content` status code with no response body. ## Best Practices for Contact Management 1. **Verify Addresses Carefully**: Always double-check cryptocurrency addresses before creating contacts. Blockchain transactions cannot be reversed if sent to the wrong address. 2. **Use Descriptive Nicknames**: Choose clear, descriptive nicknames that help you identify the purpose or owner of each contact. 3. **Test with Small Amounts**: Before sending large transactions to a new contact, verify the address works correctly by sending a small test amount. 4. **Audit Regularly**: Periodically review your contacts list to remove any that are no longer needed or have outdated information. 5. **Document Static Memo IDs**: Keep a secure record of contact IDs and their corresponding static memo IDs for ease of creating transactions. ## Understanding the Contact Response When creating or listing contacts, the API response contains several important fields: | Field | Description | | ------------------------------------------------- | -------------------------------------------------- | | `id` | Unique identifier for the contact | | `nickname` | User-friendly name you assigned to the contact | | `static_memos` | Contains blockchain-specific information | | `static_memos.[network].id` | The static memo ID used when creating transactions | | `static_memos.[network].destination_address` | The cryptocurrency address for this network | | `static_memos.[network].destination_payment_rail` | The blockchain network identifier | The `static_memos` object may contain multiple entries if the contact has addresses on different blockchain networks. # Request access to restricted features via the Meow API Source: https://developer.meow.com/guides/feature-access Request access to restricted features such as international payments, submit the eligibility details our team reviews, and check where each request stands with the Meow API. ## Prerequisites * A Meow account * An API key with the `feature-access:write` permission to request access, and `feature-access:read` to check status ## Understanding Restricted Features Some features are turned off until our team reviews your business for them. Instead of failing a call, you request access to the feature and we review the details you send. Today the API exposes one requestable feature: | Feature | Value | | ---------------------- | ------------------------ | | International payments | `international_payments` | You send the eligibility details for the feature, we notify the team that reviews them, and the request moves through these statuses: | Status | Meaning | | ---------- | --------------------------------------- | | `pending` | Awaiting review | | `approved` | The feature is enabled for your account | | `rejected` | The request was declined | Eligible businesses may be approved right away. Requesting a feature you have already requested returns its current status instead of opening a duplicate. ## Requesting Access to International Payments ### Step 1: Prepare Your Eligibility Details International payments needs an eligibility questionnaire so the team can review your request. Gather: * Your expected monthly volume and number of payments * What you will use international payments for * The destination countries you plan to send to ### Step 2: Request Access ```bash theme={null} curl -X POST "https://api.meow.com/v1/feature-access/requests" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "feature": "international_payments", "eligibility": { "monthly_volume": "from_25k_to_100k", "monthly_payment_count": "from_11_to_20", "purposes": ["supplier_payments"], "countries_served": ["GB", "DE", "KE"] } }' ``` The `eligibility` fields accept these values: | Field | Values | | ----------------------- | ---------------------------------------------------------------------------------------- | | `monthly_volume` | `less_than_10k`, `from_10k_to_25k`, `from_25k_to_100k`, `from_100k_to_500k`, `over_500k` | | `monthly_payment_count` | `ten_or_fewer`, `from_11_to_20`, `from_21_to_30`, `from_31_to_40`, `over_40` | | `purposes` | Any of `supplier_payments`, `payroll`, `customer_refunds`, `other` | | `other_purpose` | Free text describing your use when `purposes` includes `other` | | `countries_served` | One or more destination countries as ISO 3166-1 alpha-2 codes | Include `other` in `purposes` and describe it in `other_purpose` when none of the listed purposes fit. List every country you expect to pay in `countries_served`, since it drives the eligibility review. ### Step 3: Review the Response A successful response returns the feature and where the request stands: ```json theme={null} { "feature": "international_payments", "status": "pending" } ``` If your business is already eligible, `status` comes back as `approved` and the feature is enabled. Otherwise it stays `pending` until the review completes. ## Checking the Status of Your Requests List your feature access requests to see where each one stands: ```bash theme={null} curl -X GET "https://api.meow.com/v1/feature-access/requests" \ -H "x-api-key: YOUR_API_KEY" ``` Example response: ```json theme={null} { "data": [ { "feature": "international_payments", "status": "pending" } ] } ``` Poll this endpoint until the feature you requested reports `approved`, then start using it. # Create and send invoices with the Billing API Source: https://developer.meow.com/guides/invoicing Set up products and customers, then issue invoices with line items, discounts, and the payment methods you accept, one-off or recurring. Invoicing is Meow's accounts-receivable surface. You define the **products** you sell and the **customers** you bill, then combine them into an **invoice**, with its line items, due date, the payment methods you'll accept, and the account that collects the money. Meow emails the invoice to the customer on its invoice date and tracks it through to paid. Only **bank transfer** is enabled by default. Card, ACH direct debit, and USDC payment methods must be turned on for your business before you can add them to an invoice. Test the whole flow in the sandbox before going live. ## You'll need * An API key with the billing scopes for the resources you touch: * `billing:products:read` / `billing:products:write` for products * `billing:customers:read` / `billing:customers:write` for customers * `billing:invoices:read` / `billing:invoices:write` for invoices * `billing:accounts:read` to list collection accounts ## 1. Create or find a product A product is a reusable good or service with a default price. List what you already have. `GET /billing/products` returns a bare array: ```bash theme={null} curl https://api.meow.com/v1/billing/products -H "x-api-key: $MEOW_API_KEY" ``` ```json Response theme={null} [ { "id": "prod_9f8e7d6c", "name": "Catnip Consulting", "description": "Advisory retainer", "default_price": "150.00" } ] ``` Create a new one when you need it: ```bash theme={null} curl -X POST https://api.meow.com/v1/billing/products \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Catnip Consulting", "description": "Advisory retainer, billed hourly", "default_price": 150.00 }' ``` ```json Response theme={null} { "id": "prod_9f8e7d6c", "name": "Catnip Consulting", "description": "Advisory retainer, billed hourly", "default_price": "150.00" } ``` Save the product `id`. You will reference it from each invoice line item. ## 2. Create or find a customer A customer is who you invoice. `POST /billing/customers` needs a `nickname` and `email`; an `address` is optional but appears on the invoice when present. ```bash theme={null} curl -X POST https://api.meow.com/v1/billing/customers \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "nickname": "Purrfect Partners", "email": "billing@purrfectpartners.com", "address": { "street_line_1": "9 Whisker Way", "street_line_2": "Suite 200", "city": "San Francisco", "state": "CA", "postal_code": "94105", "country": "US" } }' ``` ```json Response theme={null} { "id": "cust_4a5b6c7d", "nickname": "Purrfect Partners", "email": "billing@purrfectpartners.com", "address": { "street_line_1": "9 Whisker Way", "street_line_2": "Suite 200", "city": "San Francisco", "state": "CA", "postal_code": "94105", "country": "US" }, "created_time": "2026-06-30T10:30:00.000Z", "updated_time": null } ``` Save the customer `id` for the invoice. `GET /billing/customers` lists your existing customers as a bare array. ## 3. Choose the payment methods Check which payment methods are enabled for your business. The response lists them under `allowed_types`: ```bash theme={null} curl https://api.meow.com/v1/billing/payment-method-types -H "x-api-key: $MEOW_API_KEY" ``` ```json Response theme={null} { "allowed_types": ["BANK_TRANSFER", "CARD", "ACH_DIRECT_DEBIT"] } ``` `BANK_TRANSFER` is always available. `CARD`, `ACH_DIRECT_DEBIT`, `INTERNATIONAL_WIRE`, and the USDC/USDT variants only appear once enabled for your business. Card and ACH direct debit also require customer consent to charge. ## 4. Pick a collection account Collection accounts are where invoice payments land. List the accounts available to receive funds: ```bash theme={null} curl https://api.meow.com/v1/billing/accounts -H "x-api-key: $MEOW_API_KEY" ``` ```json Response theme={null} { "accounts": [ { "id": "acct_1a2b3c4d", "display_name": "Primary Operating Account", "account_number_mask": "••1234", "status": "open", "is_primary": true, "banking_product": "GRASSHOPPER" } ] } ``` Use the account `id` as `collection_account_id` on the invoice. ## 5. Create the invoice Combine the pieces into an invoice. Each line item points at a `product_id` and a `quantity`; omit `price` or `description` to inherit the product's defaults, and add a `discount_percentage` for a per-line discount. ```bash theme={null} curl -X POST https://api.meow.com/v1/billing/invoices \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cust_4a5b6c7d", "line_items": [ { "product_id": "prod_9f8e7d6c", "quantity": 10, "price": 150.00, "description": "10 hours of advisory work", "discount_percentage": 10, "discount_description": "Early-payment discount" } ], "invoice_date": "2026-06-30", "due_date": "2026-07-30", "payment_method_types": ["BANK_TRANSFER", "CARD"], "send_email_on_creation": true, "additional_recipient_emails": ["ap@purrfectpartners.com"], "note": "Thanks for your business!", "name": "INV-2026-001", "collection_account_id": "acct_1a2b3c4d", "show_contact_address": true }' ``` ```json Response theme={null} { "id": "inv_7c8d9e0f", "name": "INV-2026-001", "customer_id": "cust_4a5b6c7d", "status": "Open", "amount": "1350.00", "amount_paid": "0.00", "amount_due": "1350.00", "line_item_ids": ["line_2b3c4d5e"], "invoice_date": "2026-06-30", "due_date": "2026-07-30", "payment_method_types": ["BANK_TRANSFER", "CARD"], "collection_account_id": "acct_1a2b3c4d", "created_at": "2026-06-30T10:30:00.000Z", "sent_at": "2026-06-30T10:30:01.000Z" } ``` | Field | Description | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `customer_id` | **Required.** The customer being invoiced. | | `line_items` | **Required.** Each references a `product_id` and `quantity`; `price`/`description` default to the product, and `discount_percentage` applies a per-line discount. | | `invoice_date` / `due_date` | **Required.** The date the invoice is scheduled to be sent and the date payment is due (`YYYY-MM-DD`). | | `payment_method_types` | **Required.** The methods to accept, a subset of `allowed_types`. | | `send_email_on_creation` | **Required.** Whether to email the customer (and `additional_recipient_emails`) on the `invoice_date`. Despite the name, the email is sent on `invoice_date`, not at create time. | | `collection_account_id` | **Required.** The account that collects payment. | | `additional_recipient_emails` | Extra addresses to copy on the invoice and its receipts. | | `recurring_schedule` | An RFC 2445 RRULE to [send the invoice on a repeating schedule](#6-send-invoices-on-a-recurring-schedule). | Use today's `invoice_date` to send the invoice now, or a future date to schedule it. The invoice stays `Scheduled` and the email goes out on the `invoice_date`. ## 6. Send invoices on a recurring schedule For subscriptions or retainers, pass a `recurring_schedule` as an RFC 2445 RRULE when you create the invoice, and Meow issues each occurrence on schedule. ```json theme={null} { "recurring_schedule": "FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=1" } ``` A tool like the [RRULE generator](https://icalendar.org/rrule-tool.html) makes it easy to build and preview the schedule string. ## 7. Retrieve and track invoices Fetch one invoice with `GET /billing/invoices/{invoice_id}`, list them all with `GET /billing/invoices` (a bare array), or download the PDF from `GET /billing/invoices/{invoice_id}/download`. Each invoice reports its `status` alongside `amount`, `amount_paid`, and `amount_due`: | `status` | Meaning | | ---------------------- | ------------------------------------------ | | `Draft` | Created but not yet sent. | | `Open` | Sent and awaiting payment. | | `Pending` | A payment is in progress. | | `Scheduled` | Queued to send on a recurring schedule. | | `Partially Paid` | Some, but not all, of the balance is paid. | | `Paid` | Paid in full; see `paid_at`. | | `Overdue` | Past `due_date` and still unpaid. | | `Canceled` / `Expired` | Voided or lapsed before payment. | ## End to end ```bash theme={null} # 1. Create a product and capture its id. PRODUCT_ID=$(curl -s -X POST https://api.meow.com/v1/billing/products \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d '{"name":"Website Build","description":"Custom website development","default_price":5000.00}' \ | jq -r .id) # 2. Create the customer. CUSTOMER_ID=$(curl -s -X POST https://api.meow.com/v1/billing/customers \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d '{"nickname":"Purrfect Partners","email":"billing@purrfectpartners.com"}' \ | jq -r .id) # 3. Pick the first available collection account. ACCOUNT_ID=$(curl -s https://api.meow.com/v1/billing/accounts \ -H "x-api-key: $MEOW_API_KEY" | jq -r '.accounts[0].id') # 4. Create and send the invoice. curl -s -X POST https://api.meow.com/v1/billing/invoices \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d "{ \"customer_id\": \"$CUSTOMER_ID\", \"line_items\": [ { \"product_id\": \"$PRODUCT_ID\", \"quantity\": 1, \"description\": \"Website build - phase 1\" } ], \"invoice_date\": \"$(date +%Y-%m-%d)\", \"due_date\": \"$(date -v+30d +%Y-%m-%d 2>/dev/null || date -d '+30 days' +%Y-%m-%d)\", \"payment_method_types\": [\"BANK_TRANSFER\"], \"send_email_on_creation\": true, \"collection_account_id\": \"$ACCOUNT_ID\" }" | jq '{ id, status, amount_due }' ``` ## See also Full request contract for `POST /billing/products`. Register who you invoice, with an optional address. Line items, discounts, payment methods, and scheduling. The accounts that can receive invoice payments. # Onboard a business entity via the API Source: https://developer.meow.com/guides/onboarding Create a business entity with a global API key and complete KYB: business details, representatives, KYC, documents, and submission via next_step. A **global** (user-level) API key can create a business entity and run its KYB onboarding without touching the dashboard: business details, the representatives (beneficial owners and officers), each representative's identity verification, supporting documents, and the final submission. Every response includes a `next_step` object that tells you what's outstanding, so you don't infer it from status. This is **self-serve** onboarding for entities your key owns. The key's user is the creator and admin. The user is not added as a representative automatically. If this user is the business signer, add the user as the primary representative in step 3. To onboard entities on behalf of *your* customers as a platform, use [Partner Onboarding](/api-reference/partner-onboarding/create-application) instead. ## You'll need * A **global** API key (user-scoped, not entity-scoped) with the `entity:create` scope for writes and `accounts:read` to read and list onboarding state. Onboarding routes reject entity-scoped keys with `403`. * No `x-entity-id` header. The entity is addressed in the URL path. **Testing through the development API.** The examples below use `https://api.dev.meow.com/v1`. This environment also exposes the [approval simulation](#sandbox-simulate-application-approval) that stands in for compliance review, so you can drive an application all the way to `approved` and open a simulated checking account. Non-production keys are granted the `simulations:write` scope automatically. For development testing, use synthetic values like these throughout the flow: | Field | Value | | -------------------------- | -------------------------------------------------------------------------------- | | Business name | `Catnip Coffee Co.` | | EIN / TIN | `102345678` | | Legal and physical address | `1209 Orange Street`, Wilmington, DE `19801`, US | | Phone | `+15551234567` | | Date of incorporation | `2020-01-18` | | Incorporation state | `DE` | | Legal structure | `llc` | | Website | `https://meow.com/` | | Description | `Accounting operations for deterministic development tests.` | | Industry | `accounting_bookkeeping` | | NAICS code | `54` | | Source of assets | `Company Revenue` | | Account purpose | `additional_yield` | | Primary representative | Leslie Knope, chief administrative officer, 100% beneficial owner | | Representative identity | DOB `1975-01-18`, phone `+12345678909`, SSN `123456789`, same Wilmington address | | Supporting documents | Synthetic `incorporation` and `ein` PDFs | ## Read `next_step` after every call Every onboarding read returns a `next_step`. For a US entity mid-onboarding it looks like this: ```json theme={null} { "step": "manage_representatives", "phase": "business_info", "missing": ["Primary representative"], "required_proof_types": ["incorporation"], "can_submit": false, "info_requests": [] } ``` | Field | Meaning | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `step` | The step the application is on now. | | `phase` | `business_info` → `ready_to_submit` → `submitted`. | | `missing` | Items you still need to provide. After submission this also lists any agreements still outstanding, which are owed alongside an open information request rather than after it. | | `required_proof_types` | KYB documents still owed, as `proof_type` values. Pass each one when requesting an upload URL. | | `can_submit` | `true` once everything required has been collected. | | `info_requests` | Open requests a reviewer raised after submission, each with its `request_id`, the documents it still wants, whether questions are outstanding, an `outstanding` list you can match on rather than parsing prose, and when it expires. | The loop: call an endpoint, then `GET /entities/{entity_id}`, act on `next_step.step`, repeat until `can_submit` is `true`, then submit. | `step` | Do this | | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `collect_business_details`, `collect_business_addresses` | [Create the entity](#1-create-the-entity), or [update its details](#2-update-business-details). | | `collect_additional_details` | [Update business details](#2-update-business-details) with the account purpose and digital-currency answers. | | `manage_representatives` | [Add representatives](#3-add-representatives), marking one `is_primary`. | | `complete_identity_verification` | [Verify each representative](#4-verify-each-representative). | | `upload_documents` | [Upload the documents](#5-upload-supporting-documents) named in `required_proof_types`. | | `complete_due_diligence` | [Complete the required due-diligence forms](#complete-required-due-diligence). Submittable forms go through the API; others must be finished in the dashboard. | | `submit_application` | [Submit the application](#6-submit-the-application). | Some entities draw an extra due-diligence step (`complete_due_diligence`) before they can submit. Forms that collect plain data are submittable through the [due-diligence endpoints](#complete-required-due-diligence); forms that require document uploads must be finished in the Meow dashboard. A follow-up information request after submission (`resolve_info_requests`) is answered through the API. Read `next_step.info_requests[]` for the `request_id`, then [fetch the form](/api-reference/onboarding/get-info-request-schema), [upload each document](/api-reference/onboarding/get-info-request-upload-url), and [answer the questions or explain a document you cannot provide](/api-reference/onboarding/submit-info-request-answers). ## 1. Create the entity `POST /entities` creates the entity and returns its public ID. The creating user is granted admin, so the same key can act on the entity immediately. Send an empty body to create a bare entity, or seed any KYB business details up front. ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/entities \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "business_name": "Catnip Coffee Co.", "business_date_of_incorporation": "2020-01-18", "legal_structure": "llc", "incorporation_state": "DE", "business_address": { "address": "1209 Orange Street", "city": "Wilmington", "state": "DE", "zip": "19801", "country": "US" } }' ``` ```json Response theme={null} { "id": "b1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "business_name": "Catnip Coffee Co.", "created_at": "2026-06-26T18:04:11.512Z" } ``` Save `id`. It's the `{entity_id}` for every call below, and the `x-entity-id` header once the entity is onboarded and you call entity-scoped APIs. `legal_structure` is one of: `ccorp`, `corp`, `scorp`, `llc`, `llp`, `lp`, `partnership`, `soleprop`, `nonprofit`, `trust`, `estate`, `foreign_entity`, `scheme`. Pass your own `reference_id` to make creation idempotent. Retrying a create with a `reference_id` you've already used returns the original entity instead of creating a duplicate, so a network timeout never leaves you with two entities. ## International businesses Meow onboards businesses in 44 countries, not just the US. The flow is identical to the walkthrough here; only a few fields differ for a business incorporated outside the US. Supported countries (ISO 3166-1 alpha-2): AD, AT, AU, BE, BM, BS, CA, CH, CL, CZ, DE, DK, ES, FR, GB, GG, GR, HR, IE, IS, IT, JE, JP, KR, LT, LU, MC, MU, NL, NO, NZ, OM, PL, PR, PT, SE, SG, SI, SK, SM, TW, US, UY, VG. A `business_address.country` outside this set is rejected at write time with a `400` that lists every supported country. What changes for a non-US business: * **`incorporation_state`**: omit it. It's the US state of incorporation and applies to US entities only. * **`business_taxid`**: send the local registration or tax number instead of an EIN (a UEN in Singapore, a UTR in the UK, a BN in Canada, and so on). * **`legal_structure`**: use `foreign_entity`. * **`business_address.country`**: the ISO code of a supported country. A state or province and a postal code are still required where the country uses them. A Singapore business, created in one call: ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/entities \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "business_name": "Catnip Coffee Pte Ltd", "business_date_of_incorporation": "2020-06-01", "legal_structure": "foreign_entity", "business_taxid": "202012345A", "business_address": { "address": "9 Raffles Place", "city": "Singapore", "state": "Central", "zip": "018936", "country": "SG" } }' ``` Two more differences appear later in the flow: * **Documents**: `next_step.required_proof_types` for a non-US business is typically `["incorporation", "shareholder_registry", "officer_registry"]`: the certificate of incorporation, the shareholder registry, and the register of directors. These apply to every non-US country. The US-only `address` proof is not requested. * **Representative KYC**: pass each representative's country-specific `id_type` and `id_number` (for example `sg_nric`, `au_passport`, `ca_sin`) instead of `us_ssn`. See [Verify each representative](#4-verify-each-representative). * **Submission**: choose `grasshopper` as the `banking_account_product_type`. Grasshopper Bank is Meow's preferred checking-account provider. `crb` is available only to US entities. Every other supported country uses `grasshopper`. Everything else, including the `next_step` loop, representatives, documents and submission, works exactly as described below. ## 2. Update business details `PATCH /entities/{entity_id}/business-details` fills in or corrects KYB details before the application is submitted. Send only the fields you want to change; anything you omit keeps its current value. Use it for the business profile the create call didn't include, and to answer the account-purpose and digital-currency questions (`collect_additional_details`). ```bash theme={null} curl -X PATCH https://api.dev.meow.com/v1/entities/$ENTITY_ID/business-details \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "business_phone": "+15551234567", "business_taxid": "102345678", "business_website": "https://meow.com/", "business_description": "Accounting operations for deterministic development tests.", "industry": "accounting_bookkeeping", "account_purpose": "additional_yield", "provides_blockchain_or_digital_currency_services": false, "transacts_in_digital_currency": false, "develops_governs_supports_blockchain_protocol": false }' ``` The response is the refreshed onboarding status, including the updated `next_step`. Send `physical_address` together with `business_address`; omitting it keeps any physical address already on file, or uses the legal address when none is set. ## 3. Add representatives `POST /entities/{entity_id}/representatives` records a beneficial owner or officer and returns its `representative_id`. This step is **create-only**. Verify the representative separately (section 4). Mark exactly one representative as the signer with `is_primary: true`; setting it demotes any previous primary. **A primary representative is required before the application can be submitted.** To promote a representative already on file, call `POST /entities/{entity_id}/representatives/{representative_id}/make-primary`. It makes that representative the signer and demotes the current one. Allowed until the application is submitted. ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/entities/$ENTITY_ID/representatives \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "email": "leslie@catnipcoffee.com", "first_name": "Leslie", "last_name": "Knope", "title_id": "chief administrative officer", "is_beneficial_owner": true, "percent_ownership": "100", "is_primary": true }' ``` ```json Response theme={null} { "representative_id": "a7c3...", "email": "leslie@catnipcoffee.com", "title_id": "chief administrative officer", "is_beneficial_owner": true, "percent_ownership": "100", "is_primary": true } ``` * `title_id` is one of: `chief executive officer`, `chief financial officer`, `chief operating officer`, `chief administrative officer`, `president`, `general partner`, `managing member`, `finance manager`. * `percent_ownership` is required when `is_beneficial_owner` is `true`, between `25` and `100`. Total beneficial ownership across representatives cannot exceed `100`. * Each `email` is unique per entity. Re-posting the same address returns `400`. ## 4. Verify each representative KYC is **per representative**, with two ways to do it. Pick one per person. Submit the representative's identity data and Meow runs verification on it. ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/entities/$ENTITY_ID/representatives/$REP_ID/kyc \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "first_name": "Leslie", "last_name": "Knope", "date_of_birth": "1975-01-18", "id_number": "123456789", "id_type": "us_ssn", "phone_number": "+12345678909", "address": { "address": "1209 Orange Street", "city": "Wilmington", "state": "DE", "zip": "19801", "country": "US" }, "ip": "203.0.113.42" }' ``` ```json Response theme={null} { "representative_id": "a7c3...", "kyc_status": "pending" } ``` For `id_type: us_ssn` send nine digits with or without dashes (`123-45-6789` or `123456789`); for `us_ssn_last_4` send the last four. `ip` is required: the end user's IPv4 or IPv6 address, forwarded to the verification provider as a fraud signal. Mint a shareable verification link and send it to the representative; their KYC completes when they finish it. ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/entities/$ENTITY_ID/representatives/$REP_ID/verification-link \ -H "x-api-key: $MEOW_API_KEY" ``` ```json Response theme={null} { "representative_id": "a7c3...", "identity_verification_url": "https://verify.meow.com/v/8f2a1c..." } ``` **Sandbox testing.** In sandbox and dev the verification provider only clears a known test identity, so use **Leslie Knope** below. The reliable path is the self-verify link (the "They self-verify" tab): mint the link and complete the flow with these exact details. A representative's `kyc_status` must reach `approved` before the application can be [approved](#sandbox-simulate-application-approval). Poll the `representatives` array to confirm. ```json theme={null} { "first_name": "Leslie", "last_name": "Knope", "date_of_birth": "1975-01-18", "id_number": "123-45-6789", "id_type": "us_ssn", "phone_number": "+12345678909", "address": { "address": "123 Main St.", "city": "Pawnee", "state": "IN", "zip": "46001", "country": "US" }, "ip": "203.0.113.42" } ``` Verification completes asynchronously. `next_step` advances once every representative has *started* verification, not once they pass, so track the actual outcome per representative via their `kyc_status` in the `representatives` array on the [onboarding status](#7-track-progress-to-submission), or the `identity_verification.*` webhooks. A representative's `kyc_status` matches the webhook stream; `action_required` means that person needs another step (for example, a document). Mint a verification link and share it so they can finish. The top-level `kyc_status` reflects the primary representative. ## 5. Upload supporting documents `next_step.required_proof_types` lists exactly which documents the entity still owes. For a US entity that is `["incorporation"]`. Each one uses a three-step flow: get a pre-signed URL, `PUT` the bytes, then confirm. Repeat the flow for each proof type: ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/entities/$ENTITY_ID/documents/upload-url \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "proof_type": "incorporation", "file_name": "certificate.pdf", "content_type": "application/pdf" }' ``` ```json Response theme={null} { "upload_url": "https://s3.amazonaws.com/...&X-Amz-Signature=...", "object_key": "kyb/b1b2.../certificate.pdf", "max_bytes": 10485760, "expires_in_seconds": 900 } ``` ```bash theme={null} curl -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" --data-binary @certificate.pdf ``` Use the same `Content-Type` you requested, keep the file under `max_bytes`, and upload before the URL expires. ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/entities/$ENTITY_ID/documents/confirm \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "object_key": "kyb/b1b2.../certificate.pdf", "proof_type": "incorporation" }' ``` ```json Response theme={null} { "confirmed": true, "file_name": "certificate.pdf" } ``` ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/entities/$ENTITY_ID/documents/upload-url \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "proof_type": "address", "file_name": "utility-bill.pdf", "content_type": "application/pdf" }' curl -X PUT "$UPLOAD_URL" -H "Content-Type: application/pdf" --data-binary @utility-bill.pdf curl -X POST https://api.dev.meow.com/v1/entities/$ENTITY_ID/documents/confirm \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "object_key": "kyb/b1b2.../utility-bill.pdf", "proof_type": "address" }' ``` Pass each value from `required_proof_types` as the `proof_type`. Before submission a US entity is asked for `incorporation`. A reviewer can ask for more after submission, and `address` is the common one: it is **proof of physical address** at the entity level, such as a utility bill, bank statement, or lease agreement evidencing the entity's relationship to its physical address. Those arrive as an information request, which `next_step.info_requests[]` reports with the `request_id` to upload against. Other common values include `articles_of_organization`, `bylaws`, `operating_agreement`, `ein`, `bank_statement`, and `beneficial_owner_id`. ### Documents for international businesses A non-US entity's `required_proof_types` usually includes two registry documents alongside `incorporation`. These prove who owns and who runs the company, the things US entities establish through their EIN and representative data instead. | `proof_type` | What to upload | Who's asked for it | | ---------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | | `incorporation` | Certificate of incorporation or formation document | Every business except UK-registered ones | | `shareholder_registry` | Your shareholder registry, also called the register of members: the official list of shareholders and their holdings | Non-US businesses (except Puerto Rico) | | `officer_registry` | Your register of directors and officers: the official list of the people who run the company | Non-US businesses (except the UK and Puerto Rico) | Whatever the country, upload only the values that appear in `next_step.required_proof_types`. That list is the source of truth for what a given entity owes. Meow may request further documents during review; those arrive as their own requirements. Address documents uploaded for a representative do not satisfy the entity-level `address` requirement: the document must evidence the *entity's* physical address. `next_step.can_submit` stays `false` until every proof type in `required_proof_types` has been uploaded. ## Complete required due diligence Some industries draw an extra **enhanced due diligence (EDD)** step before the application can be submitted. When one applies, `next_step.step` is `complete_due_diligence`. List the required forms, then for each form that's submittable through the API, fetch its schema and submit it. Forms that collect document uploads can't be submitted through the API (`requires_webapp: true`) and must be completed in the Meow dashboard. `GET /entities/{entity_id}/due-diligence` returns each required form with its completion status and whether it must be finished in the web app. ```bash theme={null} curl https://api.dev.meow.com/v1/entities/$ENTITY_ID/due-diligence \ -H "x-api-key: $MEOW_API_KEY" ``` ```json Response theme={null} { "complete": false, "forms": [ { "form_type": "investments", "complete": false, "requires_webapp": false }, { "form_type": "cannabis", "complete": false, "requires_webapp": true } ] } ``` `GET /entities/{entity_id}/due-diligence/schema/{form_type}` returns the form's pages and fields, including types, choices, validation, and conditional visibility, so you can build a valid submission. Only forms submittable through the API have a schema. ```bash theme={null} curl https://api.dev.meow.com/v1/entities/$ENTITY_ID/due-diligence/schema/investments \ -H "x-api-key: $MEOW_API_KEY" ``` `POST /entities/{entity_id}/due-diligence` submits one form. Set `form_type` to the form you're answering; `form_data` matches that form's schema. The response is the refreshed onboarding status. ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/entities/$ENTITY_ID/due-diligence \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "form_type": "investments", "form_data": { "overview": "Long-only equity fund investing in US public markets", "international_investments": false, "registered_sec": true, "current_portfolio_value": "5000000" } }' ``` Repeat for each submittable form. Once every required form is `complete`, and any `requires_webapp` forms are finished in the dashboard, `next_step` advances past `complete_due_diligence` and `can_submit` can turn `true`. ## 6. Submit the application Once `next_step.can_submit` is `true`, submit the application with `POST /entities/{entity_id}/submit`. Choose the checking account product to open. If anything is still outstanding, this returns `400` with the remaining requirements; check `next_step.can_submit` first. Use `grasshopper` for the preferred Grasshopper Bank checking account. Add `stablecoins` to `product_features` when the business also needs USDC access. Omit `product_features` when the business needs checking only. ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/entities/$ENTITY_ID/submit \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "banking_account_product_type": "grasshopper", "product_features": ["stablecoins"] }' ``` ```json Response theme={null} { "entity_id": "b1b2c3d4-...", "status": "under_review", "next_step": { "step": "resolve_info_requests", "phase": "submitted", "missing": [ "Bank Statements is required", "Source of funds is required", "patriot_act", "meow_terms_of_service" ], "required_proof_types": ["bank_statement"], "can_submit": false, "info_requests": [ { "request_id": "6f3a1b2c-8d4e-4f10-9a2b-7c5e1d3f9a80", "required_proof_types": ["bank_statement"], "questions_pending": true, "outstanding": [ { "kind": "document", "proof_type": "bank_statement", "detail": "Bank Statements is required" }, { "kind": "answers", "proof_type": null, "detail": "Source of funds is required" } ], "note": "Please send a recent bank statement", "expires_at": "2026-08-18T09:02:00Z" } ] }, "consent": { "status": "viewed", "consented_items": ["meow_privacy_policy"], "remaining_items": ["patriot_act", "meow_terms_of_service"], "viewed_at": "2026-08-11T09:14:02.000Z", "completed_at": null } } ``` Neither holds up the review. `missing` lists everything outstanding across both, and `info_requests[]` carries the per-request detail. Poll this endpoint, or subscribe to the `info_request.*` webhooks, and act on each as it appears. ### The agreements Outstanding agreements appear in `missing`, and `consent_url` is where the primary representative accepts them. Share the link. Anyone who holds it can accept for the business, so treat it as a secret. `consent` on the same response gives the detail: `remaining_items` is what is left, and `status` moves `pending` → `viewed` → `completed`. Submitting emits `onboarding_consent.updated` with `status: "pending"` and the full `remaining_items`, so a caller watching the event stream is told the agreements are owed without polling. Later events fire as the representative opens the form and accepts each item. While any agreement is outstanding, `POST /accounts` returns `403`. The review still runs. Once the representative accepts everything, they leave `missing`. Requested products also wait for their agreements. Before you simulate approval, open `consent_url`. Then wait until `consent.status` is `completed`. If you skip this step, checking can open while a requested product remains pending. ### Information requests Each entry in `next_step.info_requests[]` is a reviewer asking for something. `note` carries their message. One request can ask for documents, questions, or both, and one schema describes all of it. ```bash theme={null} REQUEST_ID=6f3a1b2c-8d4e-4f10-9a2b-7c5e1d3f9a80 curl https://api.dev.meow.com/v1/entities/$ENTITY_ID/info-requests/$REQUEST_ID/schema \ -H "x-api-key: $MEOW_API_KEY" ``` ```json Response theme={null} { "title": "Please send a recent bank statement", "pages": [ { "name": "requested_documents", "title": "Requested documents", "elements": [ { "type": "file_upload", "name": "bank_statement", "title": "Bank Statements", "description": "Please include 3 of your latest bank statements. If your bank account was created recently and you have less than 3 statements, please upload every bank statement you can.", "required": true, "file_upload_config": { "accepted_types": [".pdf", ".jpg", ".jpeg", ".png", ".txt"], "proof_type": "bank_statement", "max_files": 1 } } ] }, { "name": "followup", "title": "Follow-up", "elements": [ { "type": "text", "name": "source_of_funds", "title": "Source of funds", "required": true } ] } ] } ``` A `file_upload` element is a document. Its `file_upload_config.proof_type` is the value to pass to the upload endpoints, and `accepted_types` is what the file may be. Every other element is a question. `title` and `description` are the same wording the Meow dashboard shows, so you can put them in front of your own user rather than writing your own copy for each document type. A document that only a specific published form satisfies says so in its `description`. Use the request-scoped upload endpoints. The pre-submission `/documents/upload-url` pair is closed once you submit, and it cannot attribute a file to a request. ```bash theme={null} UPLOAD=$(curl -s -X POST \ https://api.dev.meow.com/v1/entities/$ENTITY_ID/info-requests/$REQUEST_ID/documents/upload-url \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d '{"proof_type":"bank_statement","file_name":"statement.pdf","content_type":"application/pdf"}') curl -s -X PUT "$(echo $UPLOAD | jq -r .upload_url)" \ -H "Content-Type: application/pdf" --data-binary @statement.pdf curl -s -X POST \ https://api.dev.meow.com/v1/entities/$ENTITY_ID/info-requests/$REQUEST_ID/documents/confirm \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d "{\"object_key\":\"$(echo $UPLOAD | jq -r .object_key)\",\"proof_type\":\"bank_statement\"}" ``` ```json Confirm response theme={null} { "confirmed": true, "file_name": "statement.pdf", "request_completed": false } ``` `request_completed` is `false` because the questions are still open. Post the answers keyed by the element `name` from the schema. This submits them, so send them complete. There is no draft state on this API. ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/entities/$ENTITY_ID/info-requests/$REQUEST_ID \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d '{"answers":{"source_of_funds":"Revenue from coffee sales"}}' ``` ```json Response theme={null} { "request_completed": true, "outstanding": [] } ``` ### When the business cannot provide a document Some documents a reviewer asks for do not exist. A business incorporated in a state that issues no certificate of good standing cannot upload one, and no upload will ever satisfy that request. Explain it instead, on the same endpoint. The explanation settles the document exactly as an upload does, so the request closes and onboarding moves on. **Take the key from what the request already told you.** Each entry in `outstanding` carries the `proof_type` to use, and it is the same public value as `required_proof_types`, so nothing needs mapping. ```json next_step.info_requests[0] theme={null} { "request_id": "6f3a1b2c-8d4e-4f10-9a2b-7c5e1d3f9a80", "required_proof_types": ["certificate_of_good_standing"], "questions_pending": false, "outstanding": [ { "kind": "document", "proof_type": "certificate_of_good_standing", "detail": "Certificate of Good Standing is required" } ] } ``` ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/entities/$ENTITY_ID/info-requests/$REQUEST_ID \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d '{"unavailable_proof_explanations":{"certificate_of_good_standing":"Delaware does not issue this for our entity type."}}' ``` ```json Response theme={null} { "request_completed": true, "outstanding": [] } ``` When the request also asks questions, send both in the one call: ```json Request body theme={null} { "answers": { "source_of_funds": "Revenue from coffee sales" }, "unavailable_proof_explanations": { "certificate_of_good_standing": "Delaware does not issue this for our entity type." } } ``` Omit `answers` entirely when the request only wants documents. This replaces the explanations on file rather than adding to them, so send every explanation the request should keep. An explanation for a document you have since uploaded is dropped, because the upload already settled it. This call needs the `entity:create` scope on an admin key. Reading the request needs only `accounts:read`. Four things return `400`: | Cause | Fix | | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | | A `proof_type` the request never asked for | Use a value from `required_proof_types`. | | Blank text, or longer than 1000 characters | Send a real explanation within the limit. | | Neither `answers` nor `unavailable_proof_explanations` | Send at least one. | | Different explanations on an already completed request | Resending identical ones is a safe retry. Anything else needs a new request from the reviewer. | An explanation closes the request on your say-so. A reviewer reads it afterwards and can raise a new information request if it does not hold up, so treat it as a statement of fact about the business, not a way to skip a document you can obtain. Once a request is satisfied, it leaves `next_step.info_requests[]`. With nothing else outstanding, `step` returns to `monitor_review`. ```json theme={null} { "status": "under_review", "next_step": { "step": "monitor_review", "phase": "submitted", "missing": [], "required_proof_types": [], "can_submit": false, "info_requests": [] } } ``` A request can expire. `next_step.info_requests[]` carries `expires_at` alongside `request_id`, `required_proof_types`, `questions_pending`, `outstanding`, and the reviewer's `note`. Uploads against an expired or completed request are rejected, and the request stops appearing in the list. ### Reaching approved `status` leaves `under_review` when the bank finishes, not when you clear these: * `approved`: the business application is approved and its checking account is active. * `submitted`: KYB passed and the account is still opening. * `rejected`: the application was declined. Watch for the outcome with the `application.*` webhooks, or by polling `GET /entities/{entity_id}`. `approved` does not mean the queue is empty. Approval does not wait on the agreements, so an approved business can still owe them, and an information request raised before approval stays open after it. `approved` also does not mean that every requested product is ready. Stablecoin access has separate approval and account-creation steps. Do not run the [stablecoin funding simulation](/api-reference/sandbox/simulate-stablecoin-account-funding) until the stablecoin financial account exists. `next_step.step` keeps naming the work while any is left, so it never contradicts `missing`: an approved business that still owes its agreements reads `accept_terms`, and one with an open request reads `resolve_info_requests`. `step` becomes `onboarding_complete` only once `missing` and `info_requests[]` are both empty. A `rejected` application owes nothing, so it reports `onboarding_complete` immediately. ## Sandbox: simulate application approval In sandbox and development environments there is no human compliance review, so a submitted application would otherwise sit in `under_review` forever. Simulate the approval with `POST /simulations/entities/{entity_id}/application_approval`. It approves the application as if compliance review had passed. It also approves each requested product feature and starts checking-account onboarding. ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/simulations/entities/$ENTITY_ID/application_approval \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "banking_account_product_type": "grasshopper" }' ``` * **Every representative's `kyc_status` must be `approved` first.** Real review never approves an application whose beneficial owner failed KYC, and neither does the simulation: it returns `400` and names how many representatives still need to pass. Poll `GET /entities/{entity_id}` and confirm every entry in the `representatives` array shows `kyc_status: "approved"` before calling this. In sandbox, get there with the [verification link](#4-verify-each-representative) and the test identity. * The application must already be submitted (section 6). * Complete the agreements at `consent_url` before this call when the application requests another product. Otherwise, checking can open while that product remains pending. * The body is optional. `banking_account_product_type` defaults to `grasshopper`, Meow's preferred provider. `crb` is also supported for eligible businesses. * Use an **entity** API key scoped to the target entity, or a **global** (user) API key with the `simulations:write` scope (granted automatically on non-production keys); for the global key, the key's user must be an admin of the entity. After calling it, the application's `status` moves to `submitted` immediately and to `approved` once the checking account activates. Poll `GET /entities/{entity_id}` or subscribe to the `application.*` webhook events to observe the transition. Requested stablecoin onboarding continues asynchronously after the Grasshopper account opens. The simulation approves the request, but it does not make the stablecoin financial account available in the same response. The call is safe to repeat. If account opening stalls in the sandbox (the status sits at `submitted` for more than a few minutes), call it again. A retry completes the stalled opening and adopts the account the sandbox bank already created, without opening a duplicate. Simulation endpoints are only available in **sandbox and development** environments. In production they return `404 Not Found`. ## 7. Track progress to submission `GET /entities/{entity_id}` returns the lifecycle `status`, uploaded `documents`, `next_step`, and a `representatives` array with each representative's `kyc_status`. The top-level `kyc_status` reflects the primary representative. ```bash theme={null} curl https://api.dev.meow.com/v1/entities/$ENTITY_ID -H "x-api-key: $MEOW_API_KEY" ``` ```json Response theme={null} { "entity_id": "b1b2c3d4-...", "onboarding_type": "api", "status": "pending_user_action", "kyc_status": "pending", "business_name": "Catnip Coffee Co.", "created_at": "2026-06-26T18:04:11.512Z", "updated_at": "2026-06-26T18:22:54.001Z", "documents": [ { "proof_type": "incorporation", "file_name": "certificate.pdf" }, { "proof_type": "address", "file_name": "utility-bill.pdf" } ], "representatives": [ { "representative_id": "a7c3...", "first_name": "Leslie", "last_name": "Knope", "email": "leslie@catnipcoffee.com", "is_primary": true, "is_beneficial_owner": true, "percent_ownership": "100", "kyc_status": "pending" } ], "next_step": { "step": "submit_application", "phase": "ready_to_submit", "missing": [], "required_proof_types": [], "can_submit": true } } ``` | `status` | Meaning | | ------------------------- | -------------------------------------- | | `pending_document_upload` | No documents uploaded yet. | | `pending_user_action` | Still collecting required data. | | `submitted` | Submitted; queued for review. | | `under_review` | Meow is reviewing the application. | | `approved` | KYB approved (and an account is open). | | `rejected` | KYB rejected. | ## 8. List your entities `GET /entities` returns every entity your key's user owns, each tagged with its `onboarding_type`, with opaque-offset pagination. ```bash theme={null} curl "https://api.dev.meow.com/v1/entities?limit=20" -H "x-api-key: $MEOW_API_KEY" ``` ```json Response theme={null} { "entities": [ { "entity_id": "b1b2c3d4-...", "onboarding_type": "api", "status": "under_review", "business_name": "Catnip Coffee Co.", "created_at": "...", "updated_at": "..." } ], "page": { "nextOffset": "20" } } ``` When `page` is non-null, pass `page.nextOffset` as the `offset` query parameter for the next page. When `page` is `null`, you've reached the end. ## Track updates with webhooks Instead of polling `GET /entities/{entity_id}`, subscribe once and let Meow push each change. A global key's own onboarding events, `identity_verification.*` for KYC outcomes and `application.*` for KYB lifecycle changes, deliver to a regular webhook subscription, so the same key you onboard with can receive them. ```bash theme={null} curl -X POST https://api.dev.meow.com/v1/webhooks/subscriptions \ -H "x-api-key: $MEOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Onboarding tracker", "url": "https://example.com/webhooks/meow", "event_types": ["identity_verification.approved", "identity_verification.rejected", "identity_verification.action_required", "application.submitted", "application.under_review", "application.approved", "application.rejected"] }' ``` Save the `signing_secret` from the response. It is returned once. Omit `event_types` (or send `null`) to receive every event the key can see. Each delivery carries the same `status` values you would otherwise poll for, so you can drive the whole flow off the stream. The key needs the `webhooks:write` and `webhooks:read` scopes, and the `url` must be public HTTPS. See the [webhooks guide](/guides/webhooks) for signature verification, retries, and out-of-order handling. ## End to end ```bash theme={null} # Development base URL, which drives the whole flow to `approved` below. MEOW_API="https://api.dev.meow.com/v1" # 1. Create the entity with its business details. ENTITY_ID=$(curl -s -X POST $MEOW_API/entities \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d '{"business_name":"Catnip Coffee Co.","business_date_of_incorporation":"2020-01-18","legal_structure":"llc","incorporation_state":"DE","business_address":{"address":"1209 Orange Street","city":"Wilmington","state":"DE","zip":"19801","country":"US"}}' \ | jq -r .id) # 2. Fill in the remaining business profile. curl -s -X PATCH $MEOW_API/entities/$ENTITY_ID/business-details \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d '{"business_phone":"+15551234567","business_taxid":"102345678","business_website":"https://meow.com/","business_description":"Accounting operations for deterministic development tests.","industry":"accounting_bookkeeping","account_purpose":"additional_yield","provides_blockchain_or_digital_currency_services":false,"transacts_in_digital_currency":false,"develops_governs_supports_blockchain_protocol":false}' # 3. Add the primary representative. REP_ID=$(curl -s -X POST $MEOW_API/entities/$ENTITY_ID/representatives \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d '{"email":"leslie@catnipcoffee.com","title_id":"chief administrative officer","is_beneficial_owner":true,"percent_ownership":"100","is_primary":true}' \ | jq -r .representative_id) # 4. Verify them. In sandbox, mint a self-serve link and complete it with the # Leslie Knope test identity, the reliable path to a passing KYC. Open the # printed URL and finish the flow before continuing. curl -s -X POST $MEOW_API/entities/$ENTITY_ID/representatives/$REP_ID/verification-link \ -H "x-api-key: $MEOW_API_KEY" | jq -r .identity_verification_url # 5. Upload exactly the documents named in next_step.required_proof_types. # Use the correct document bytes for each proof type in your integration. for PROOF_TYPE in $(curl -s $MEOW_API/entities/$ENTITY_ID \ -H "x-api-key: $MEOW_API_KEY" | jq -r '.next_step.required_proof_types[]'); do FILE_NAME="${PROOF_TYPE}.pdf" UPLOAD=$(curl -s -X POST $MEOW_API/entities/$ENTITY_ID/documents/upload-url \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d "{\"proof_type\":\"$PROOF_TYPE\",\"file_name\":\"$FILE_NAME\",\"content_type\":\"application/pdf\"}") curl -s -X PUT "$(echo $UPLOAD | jq -r .upload_url)" \ -H "Content-Type: application/pdf" --data-binary "@$FILE_NAME" curl -s -X POST $MEOW_API/entities/$ENTITY_ID/documents/confirm \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d "{\"object_key\":\"$(echo $UPLOAD | jq -r .object_key)\",\"proof_type\":\"$PROOF_TYPE\"}" done # 6. Poll until ready. This walkthrough covers the common path with no enhanced # due diligence: KYC clears asynchronously and every required document must be # uploaded before can_submit turns true. If next_step.step becomes # "complete_due_diligence", the loop stops so you can submit the required # forms (see "Complete required due diligence") before re-running. while true; do STATUS=$(curl -s $MEOW_API/entities/$ENTITY_ID -H "x-api-key: $MEOW_API_KEY") [ "$(echo "$STATUS" | jq -r .next_step.can_submit)" = "true" ] && break if [ "$(echo "$STATUS" | jq -r .next_step.step)" = "complete_due_diligence" ]; then echo "Enhanced due diligence required. Submit the forms above, then re-run." >&2 exit 1 fi sleep 5 done # 7. Submit for Grasshopper checking and USDC stablecoins. SUBMISSION=$(curl -s -X POST $MEOW_API/entities/$ENTITY_ID/submit \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d '{"banking_account_product_type":"grasshopper","product_features":["stablecoins"]}') echo "$SUBMISSION" | jq '{status, consent_url}' # 8. Open consent_url. The primary representative must accept all agreements. echo "Open this consent URL: $(echo "$SUBMISSION" | jq -r .consent_url)" while [ "$(curl -s $MEOW_API/entities/$ENTITY_ID -H "x-api-key: $MEOW_API_KEY" \ | jq -r .consent.status)" != "completed" ]; do sleep 5 done # 9. Approval is gated on KYC. Wait until every representative has passed. while [ "$(curl -s $MEOW_API/entities/$ENTITY_ID -H "x-api-key: $MEOW_API_KEY" \ | jq '[.representatives[] | select(.kyc_status != "approved")] | length')" != "0" ]; do sleep 5 done # 10. Simulate approval. This approves requested features and starts Grasshopper. curl -s -X POST $MEOW_API/simulations/entities/$ENTITY_ID/application_approval \ -H "x-api-key: $MEOW_API_KEY" -H "Content-Type: application/json" \ -d '{"banking_account_product_type":"grasshopper"}' | jq .status # 11. Poll until the Grasshopper account activates. while [ "$(curl -s $MEOW_API/entities/$ENTITY_ID -H "x-api-key: $MEOW_API_KEY" | jq -r .status)" != "approved" ]; do sleep 5 done echo "Entity $ENTITY_ID and its Grasshopper account are approved." # Stablecoin onboarding continues asynchronously. Wait until the stablecoin # financial account exists before you use the funding simulation. ``` ## See also Full request contract for `POST /entities`. Fill in or correct KYB details before submission. Beneficial owners and officers, with the primary signer flag. Choose the account product, then send for review. # Send book, crypto, and international payments with Meow Source: https://developer.meow.com/guides/transfers Move funds with the Meow API: internal book transfers, crypto transfers to contacts, and international payments in local currency. This guide explains how to move funds with the Meow API. It covers: * Internal book transfers between bank accounts * Crypto transfers to cryptocurrency addresses * International payments in a recipient's local currency ## Prerequisites * A Meow account with enabled bank accounts * API key permissions: * Book transfers: `accounts:read` and `transfers:book:write` * Crypto transfers: `accounts:read`, `accounts:payment-networks`, and `transfers:crypto:write` * International payments: `contacts:read`, `contacts:write`, and `transfers:international:write` * A cryptocurrency contact for crypto transfers (see [Contact Management Guide](/guides/contacts)) ## Authentication Overview The headers you send depend on the type of API key: * **Entity API keys** scope all requests to a single entity. No `x-entity-id` header is required and you can only move funds between accounts that belong to that entity. * **Global API keys** can access multiple entities, but every request must include an `x-entity-id` header that identifies the source entity for the request. Use `GET /v1/api-keys/accessible-entities` to discover which entities you can access. When booking transfers, the destination account can belong to any other entity you have access to, as long as the accounts remain compatible. Regardless of the key type, book transfers can only be created between accounts that share the same `bankProductType`. ## Book Transfers Book transfers move funds internally between two accounts you control. The source account is chosen by the path parameter, and the destination is provided in the request body. ### Workflow with Entity API Keys **1. List accounts** to find eligible source and destination accounts: ```bash theme={null} curl -X GET "https://api.meow.com/v1/accounts" \ -H "x-api-key: YOUR_ENTITY_API_KEY" ``` Record each account's `accountId` and `bankProductType`. You must choose a destination account with the same `bankProductType` as the source. **2. Create the book transfer:** ```bash theme={null} curl -X POST "https://api.meow.com/v1/accounts/{source_account_id}/book" \ -H "x-api-key: YOUR_ENTITY_API_KEY" \ -H "Idempotency-Key: book-transfer-20240318-001" \ -H "Content-Type: application/json" \ -d '{ "amount": "250.00", "to_account_id": "63fe8c62-58db-4a48-a42d-625b79a7f3fe", "description": "Move excess cash to interest account" }' ``` Replace `{source_account_id}` and `to_account_id` with the account IDs you gathered. The `Idempotency-Key` must be unique per transfer attempt. Example response: ```json theme={null} { "id": "bt_9f0a6c32", "amount": "250.00", "description": "Move excess cash to interest account", "created_time": 1718132817, "transfer_type": "BOOK", "to_account_id": "63fe8c62-58db-4a48-a42d-625b79a7f3fe" } ``` ### Workflow with Global API Keys **1. Discover accessible entities:** ```bash theme={null} curl -X GET "https://api.meow.com/v1/api-keys/accessible-entities" \ -H "x-api-key: YOUR_GLOBAL_API_KEY" ``` Use the returned IDs to decide which entity you are operating on. **2. List accounts for a specific entity:** ```bash theme={null} curl -X GET "https://api.meow.com/v1/accounts" \ -H "x-api-key: YOUR_GLOBAL_API_KEY" \ -H "x-entity-id: ENTITY_ID" ``` Locate source accounts within the selected entity that share the same `bankProductType` as potential destination accounts. Repeat this call for each entity you have access to so you can identify compatible cross-entity pairs. **3. Create the book transfer** using the source entity header: ```bash theme={null} curl -X POST "https://api.meow.com/v1/accounts/{source_account_id}/book" \ -H "x-api-key: YOUR_GLOBAL_API_KEY" \ -H "x-entity-id: ENTITY_ID" \ -H "Idempotency-Key: bt-entity-456-20240318" \ -H "Content-Type: application/json" \ -d '{ "amount": "500.00", "to_account_id": "9de5402c-493d-419e-afce-87d2c02e8d09", "description": "Sweep to payroll" }' ``` The `x-entity-id` value must match the source account's entity. As long as you have access, the destination account can belong to a different entity that shares the same `bankProductType`. ### Book Transfer Tips * Pick a fresh `Idempotency-Key` header for each attempt so retries do not create duplicate transfers. The legacy `idempotency_key` body field still works but is deprecated; when both are sent they must match. * Verify both accounts are active, denominated in the same currency, and share a `bankProductType` even when they belong to different entities. * Transfers fail if the destination account's `bankProductType` differs from the source. * API-based transfers are blocked when Security Policy Spend controls are enabled for the entity; adjust the policy or rely on the dashboard instead. ## Crypto Transfers Crypto transfers send funds from an eligible checking or global account to a cryptocurrency contact. The destination determines the currency and network. ### Step 1: List Accounts ```bash theme={null} curl -X GET "https://api.meow.com/v1/accounts" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-entity-id: ENTITY_ID_IF_GLOBAL" ``` Example response: ```json theme={null} { "accounts": [ { "depositAccount": { "accountId": "acct_123456789", "accountType": "CHECKING", "productName": "Business Checking", "bankProductType": "Grasshopper", "status": "OPEN", "currency": { "currencyCode": "USD" }, "accountNumberDisplay": "1234" } } ] } ``` ### Step 2: Verify Crypto Support ```bash theme={null} curl -X GET "https://api.meow.com/v1/accounts/{account_id}/payment-networks" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-entity-id: ENTITY_ID_IF_GLOBAL" ``` Look for a payment network with `"transferOut": true`. Its `type` and network fields show which transfers the account can originate. ### Step 3: Find the Destination List your contacts and select a crypto payment method. Use its `crypto_id` with the current endpoint. You can also use a `static_memo_id` from a contact. ```bash theme={null} curl -X GET "https://api.meow.com/v1/contacts" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-entity-id: ENTITY_ID_IF_GLOBAL" ``` ### Step 4: Create the Crypto Transaction ```bash theme={null} curl -X POST "https://api.meow.com/v1/accounts/{account_id}/crypto" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-entity-id: ENTITY_ID_IF_GLOBAL" \ -H "Idempotency-Key: crypto-transfer-20260821-001" \ -H "Content-Type: application/json" \ -d '{ "amount": "100.00", "crypto_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "internal_note": "Payment for treats", "emails": ["simba@tiger.com"] }' ``` Example response: ```json theme={null} { "id": "tx_987654321", "status": "processing", "amount": "100.00", "internal_note": "Payment for treats", "created_time": 1634567890, "destination_address": "0x1234567890abcdef1234567890abcdef12345678", "destination_payment_rail": "ethereum", "destination_address_nickname": "Vendor Wallet", "approval_id": "3f8d54e8-7ed0-4a79-9cf7-a12d8e57cf02", "message": null } ``` The `Idempotency-Key` header is optional for compatibility. Send one for every transfer so a retry cannot create a duplicate request. ### Transaction Status Possible statuses after submission: | Status | Description | | ------------ | ------------------------------------------ | | `pending` | Awaiting processing | | `processing` | In progress | | `sent` | Successfully transmitted to the blockchain | | `error` | Encountered an issue | | `canceled` | Canceled by the user or system | | `void` | Reversed before settlement | ## International Payments International payments pay a contact in their own currency, converting from US dollars at the rate quoted when you send. To send US dollars instead, including to a bank outside the US, use a wire. ### Step 1: Find out what the destination needs Bank details differ by destination. Some countries route on an IBAN, others on a domestic routing code, and some also want the recipient's address. Ask before you build the contact: ```bash theme={null} curl -X GET "https://api.meow.com/v1/contacts/international-requirements?recipient_currency=EUR&bank_country=DE" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-entity-id: ENTITY_ID_IF_GLOBAL" ``` Check `supported` first, since not every currency reaches every country. When it is `false`, `unsupported_reason` says why. When it is `true`, `required_fields` lists exactly the fields to collect, each with an optional `pattern` to validate against. ### Step 2: Create the contact Send the fields the previous step listed as `international` on the contact. Provide exactly one of `international`, `counterparty`, or `crypto_address`: ```bash theme={null} curl -X POST "https://api.meow.com/v1/contacts" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-entity-id: ENTITY_ID_IF_GLOBAL" \ -H "Content-Type: application/json" \ -d '{ "nickname": "Berlin supplier", "international": { "payment_method": "local", "beneficiary_type": "business", "currency": "EUR", "company_name": "Example GmbH", "bank_country": "DE", "iban": "DE89370400440532013000", "bic_swift": "DEUTDEFF" } }' ``` ### Step 3: Quote the payment Quoting prices the payment without sending it, so you can show the rate, the fee, and the total debit before committing. Fix either leg: pass `recipient_amount` to send an exact amount in their currency, or `source_amount` to spend an exact number of US dollars. ```bash theme={null} curl -X POST "https://api.meow.com/v1/accounts/{account_id}/international/quote" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-entity-id: ENTITY_ID_IF_GLOBAL" \ -H "Content-Type: application/json" \ -d '{ "contact_id": "contact_...", "recipient_currency": "EUR", "recipient_amount": "1000.00" }' ``` The response returns `rate`, `fee`, `total_debit`, and `fixed_side` naming the leg you fixed. A quote reserves nothing. The payment is priced again when you send it, so the figures can move if the rate does. ### Step 4: Send the payment ```bash theme={null} curl -X POST "https://api.meow.com/v1/accounts/{account_id}/international" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-entity-id: ENTITY_ID_IF_GLOBAL" \ -H "Idempotency-Key: UNIQUE_KEY" \ -H "Content-Type: application/json" \ -d '{ "contact_id": "contact_...", "recipient_currency": "EUR", "recipient_amount": "1000.00", "purpose": "Invoice 4471" }' ``` `purpose` is required and describes why you are paying. Some destinations also want a `purpose_code`, and `invoice_number`, `invoice_date` and `reference` are available where the recipient's bank expects them. The response returns `approval_id` and a `status` of either `processing`, meaning it is on its way, or `pending_approval`, meaning the entity's security policy requires someone to approve it before it moves. The `Idempotency-Key` header is required on international payments, so a retried request cannot send a second payment. ## Checking Transfer Status After initiating a transfer, use the corresponding GET endpoint with the transfer ID returned from the create call to check its current status. ### ACH Transfers Use the `id` from the create ACH response (e.g. `ach_...`) or the transaction group ID (e.g. `withdrawal_ach_...`) from a transaction listing: ```bash theme={null} curl -X GET "https://api.meow.com/v1/accounts/{account_id}/achs/{ach_transfer_id}" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-entity-id: ENTITY_ID_IF_GLOBAL" ``` ### Wire Transfers Use the transaction group ID for the wire transfer: ```bash theme={null} curl -X GET "https://api.meow.com/v1/accounts/{account_id}/wires/{wire_transfer_id}" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-entity-id: ENTITY_ID_IF_GLOBAL" ``` ### Crypto Transactions Use the `id` from the create crypto response: ```bash theme={null} curl -X GET "https://api.meow.com/v1/accounts/{account_id}/crypto/{transaction_id}" \ -H "x-api-key: YOUR_API_KEY" \ -H "x-entity-id: ENTITY_ID_IF_GLOBAL" ``` These GET endpoints return details for both incoming and outgoing transfers. For example, you can retrieve details of an incoming ACH deposit using the same endpoint used to check the status of an outgoing ACH transfer you initiated. ## Attach your own metadata Add `metadata` when you create an ACH, wire, book, crypto, international, or scheduled ACH transfer. Use it for identifiers from your system that must stay with the payment: ```json theme={null} { "amount": "250.00", "metadata": { "invoice_id": "inv_4471", "batch_id": "payroll_2026_09" } } ``` Metadata accepts up to 20 string pairs. Each key can contain up to 40 characters, each value can contain up to 200 characters, and the complete object can contain up to 5 KB. Meow stores this object unchanged. It is available in the create response, `GET /approvals/{approval_id}`, the transfer read endpoint, and snapshot webhook events. For a scheduled ACH transfer, the schedule and every payment that it creates carry the same metadata. Metadata is `null` when the create request did not include it. ## Best Practices * **Verify recipients**: Confirm cryptocurrency addresses and contact payment methods before initiating transfers. * **Use meaningful references**: Populate `description` (book transfers) or `internal_note` (crypto transfers) with reconciliation-friendly values. * **Start small**: Send a nominal amount when onboarding a new recipient. * **Protect secrets**: Keep API keys and transaction data out of unsecured channels. * **Monitor statuses**: Follow up with support if a transfer remains in `error`. # Receive webhook events from Meow Source: https://developer.meow.com/guides/webhooks Subscribe to Meow webhook events, verify HMAC signatures, handle retries and idempotency, and choose between snapshot and thin payload delivery modes. Meow POSTs an event to your URL when a transfer changes state or a deposit clears. The wire format is [Standard Webhooks](https://www.standardwebhooks.com/): the [Python](https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries/python), [Node](https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries/javascript), and [Go](https://github.com/standard-webhooks/standard-webhooks/tree/main/libraries/go) verifier libraries work as-is. ## You'll need * An API key with `webhooks:write` and `webhooks:read`. * A public HTTPS URL. Private, loopback, and metadata IPs are blocked at both create time and every delivery. ## 1. Create a subscription Save `secret` from the response. Meow returns it once. Every webhook `POST` requires an `Idempotency-Key` header, so a retried create never leaves you with two subscriptions. Generate a fresh key per operation: 1-50 printable ASCII characters, no spaces. Reusing one is rejected with a `400`. ```bash theme={null} curl -X POST https://api.meow.com/v1/webhooks/subscriptions \ -H "x-api-key: $MEOW_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "name": "Prod webhook receiver", "url": "https://example.com/webhooks/meow", "event_types": ["ach_transfer.updated", "wire_transfer.updated"], "payload_mode": "snapshot" }' ``` ```json Response theme={null} { "id": "7b9e8a5f-3c2d-4d11-9a8c-1e6c4f2a5b30", "name": "Prod webhook receiver", "url": "https://example.com/webhooks/meow", "event_types": ["ach_transfer.updated", "wire_transfer.updated"], "payload_mode": "snapshot", "is_enabled": true, "secret": "whsec_..." } ``` `event_types: null` subscribes to everything. Pass an array to allowlist (empty array is rejected). You can only list events this subscription can receive: the rail events in the [catalog](/api-reference/webhooks/events) plus your own onboarding events, `application.*` and `identity_verification.*` (so a global key can track an entity it is onboarding without polling). Only the delivery-meta events `webhook.test` and `message.attempt.exhausted` are rejected with a 400. They are always-on or on-demand, not subscribable. Subscriptions belong to an entity, not to one account. Every enabled subscription for the entity receives each matching event. Use `data.account_id` to route crypto send and deposit events to the correct account. ## 2. Verify the signature Three headers come with every delivery: | Header | What it is | | ------------------- | ----------------------------------------------------------------------------------------- | | `webhook-id` | Delivery ID. Same value on every retry. | | `webhook-timestamp` | Epoch seconds. New on each attempt. | | `webhook-signature` | One or more `v1,` entries, space-separated. Multiple during secret rotation. | Sign `f"{webhook-id}.{webhook-timestamp}.{body}"` with HMAC-SHA-256. The HMAC key is the **base64-decoded** body of your `whsec_` secret, not the raw string. Reject anything more than 5 minutes off. That is the replay window. ```python Python theme={null} import hmac, hashlib, base64, time from fastapi import HTTPException SECRET = "whsec_..." # rotate via PATCH /webhooks/subscriptions/{id} # Strip the `whsec_` prefix and base64-decode the rest to get the HMAC key. _HMAC_KEY = base64.b64decode(SECRET.removeprefix("whsec_")) def verify(request_body: bytes, msg_id: str, ts: str, sig_header: str) -> None: if abs(int(ts) - int(time.time())) > 300: raise HTTPException(400, "stale timestamp") signed = f"{msg_id}.{ts}.{request_body.decode()}".encode() expected = base64.b64encode( hmac.new(_HMAC_KEY, signed, hashlib.sha256).digest() ).decode() # `webhook-signature` may carry multiple `v1,` during rotation, # match any of them. for token in sig_header.split(): version, _, candidate = token.partition(",") if version == "v1" and hmac.compare_digest(candidate, expected): return raise HTTPException(400, "bad signature") ``` ```javascript Node theme={null} import crypto from "node:crypto"; const SECRET = process.env.WEBHOOK_SECRET; // Strip `whsec_` and base64-decode to get the HMAC key. const HMAC_KEY = Buffer.from(SECRET.replace(/^whsec_/, ""), "base64"); export function verify(body, msgId, ts, sigHeader) { if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) { throw new Error("stale timestamp"); } const expected = Buffer.from( crypto .createHmac("sha256", HMAC_KEY) .update(`${msgId}.${ts}.${body}`) .digest("base64"), ); // `webhook-signature` may carry multiple `v1,` during rotation, // guard buffer length first because `timingSafeEqual` throws on mismatched // lengths, which would 500 on a malformed token before checking the next. const ok = sigHeader .split(/\s+/) .filter(Boolean) .some((token) => { const [version, sig] = token.split(","); if (version !== "v1" || !sig) return false; const candidate = Buffer.from(sig); return ( candidate.length === expected.length && crypto.timingSafeEqual(candidate, expected) ); }); if (!ok) throw new Error("bad signature"); } ``` ```go Go theme={null} package webhooks import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "errors" "fmt" "strconv" "strings" "time" ) // Strip the `whsec_` prefix and base64-decode the rest to get the HMAC key. func decodeSecret(secret string) ([]byte, error) { return base64.StdEncoding.DecodeString(strings.TrimPrefix(secret, "whsec_")) } func Verify(body []byte, msgID, ts, sigHeader, secret string) error { key, err := decodeSecret(secret) if err != nil { return fmt.Errorf("bad secret: %w", err) } epoch, err := strconv.ParseInt(ts, 10, 64) if err != nil { return fmt.Errorf("bad timestamp: %w", err) } if d := time.Now().Unix() - epoch; d > 300 || d < -300 { return errors.New("stale timestamp") } mac := hmac.New(sha256.New, key) fmt.Fprintf(mac, "%s.%s.%s", msgID, ts, body) expected := base64.StdEncoding.EncodeToString(mac.Sum(nil)) for _, token := range strings.Fields(sigHeader) { if v, sig, ok := strings.Cut(token, ","); ok && v == "v1" { if hmac.Equal([]byte(sig), []byte(expected)) { return nil } } } return errors.New("bad signature") } ``` Use constant-time compare (`hmac.compare_digest`, `crypto.timingSafeEqual`, `hmac.Equal`). `==` leaks the secret one byte at a time. ## 3. Dispatch on `payload.status` Event names stay coarse: `{resource}.created` and `{resource}.updated`. The lifecycle stage lives on the payload, so one handler covers the whole flow: ```python theme={null} def handle_ach(event: dict) -> None: transfer = event["data"] match transfer["status"]: case "pending" | "processing": mark_in_flight(transfer["id"]) case "sent": mark_settled(transfer["id"]) case "returned" | "error": alert_ops(transfer["id"], reason=transfer.get("error")) case "canceled": mark_canceled(transfer["id"]) ``` Per-event payload shapes are in the [event catalog](/api-reference/webhooks/events). ## 4. Pick a payload mode Set `payload_mode` per subscription. Change it any time with `PATCH`. `data` carries the full resource. No follow-up GET needed. ```json theme={null} { "type": "ach_transfer.updated", "timestamp": "2026-04-28T08:00:00Z", "data": { "id": "withdrawal_txc_15wp3bd309xenf6p", "object": "ach_transfer", "status": "sent", "amount": "1500.00", "counterparty_name": "Acme Corp", "metadata": { "invoice_id": "inv_4471" }, "...": "..." }, "sequence": 4 } ``` `data` carries only `{id, object}`. Your handler fetches the current state from the REST API, so out-of-order retries can't show you stale data. The fetched payment includes any metadata from its create request. ```json theme={null} { "type": "ach_transfer.updated", "timestamp": "2026-04-28T08:00:00Z", "data": { "id": "withdrawal_txc_15wp3bd309xenf6p", "object": "ach_transfer" }, "sequence": 4 } ``` Test events and `message.attempt.exhausted` have no `id` to GET, so they always come through with the full body. ## 5. Handle out-of-order deliveries Deliveries are **not ordered**. Retries, redrives, and concurrent workers mean an older state for a resource can land after a newer one. Two fields let you stay correct. Every resource event carries a **`sequence`**, a counter that increments once per resource, the same for every subscriber, stable across retries and redrives. Track the highest `sequence` you've applied **per resource** and advance it with a single **atomic conditional write**, so a stale delivery can't overwrite newer state: ```python theme={null} def handle(event: dict) -> None: data = event["data"] state = fetch_state(data) # in thin mode, GET the resource by data["id"] first # Apply the new state and advance the high-water mark in ONE atomic # conditional write, keyed per resource. The WHERE clause makes the # database the arbiter: concurrent workers race the same row, and an # older sequence updates zero rows. Don't read-then-write in separate # steps: two workers can read the same mark, both pass the check, and # the older one lands last, clobbering newer state. db.execute( """ INSERT INTO resource_state (object, id, state, last_sequence) VALUES (%(object)s, %(id)s, %(state)s, %(sequence)s) ON CONFLICT (object, id) DO UPDATE SET state = EXCLUDED.state, last_sequence = EXCLUDED.last_sequence WHERE resource_state.last_sequence < EXCLUDED.last_sequence """, { "object": data["object"], "id": data["id"], "state": state, "sequence": event["sequence"], }, ) # Zero rows updated → a newer sequence already landed; this delivery is stale. ``` `sequence` is **monotonic, not gapless**. A hole (you see 4 then 7) is normal. Not every internal change emits an event, and a redelivered event keeps its original number. Never wait for a missing number or treat a gap as lost data; only the per-resource ordering is guaranteed. `sequence` is absent on `webhook.test` and `message.attempt.exhausted` (they describe no resource). For everything else, pair it with deduplication on `webhook-id` (next section): `sequence` discards stale states, `webhook-id` discards exact duplicates. ## 6. Retries Meow retries up to **10 times** over \~91 hours. | Attempt | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | | ------------ | - | --- | --- | ---- | --- | --- | ---- | ---- | ---- | ---- | | Delay before | 0 | 5 s | 5 m | 30 m | 2 h | 5 h | 10 h | 14 h | 20 h | 24 h | * Each non-zero delay gets up to 20% extra jitter. * `Retry-After` on a 429 or 503 is honored, up to 24 hours. * 5 failures in a row open a circuit breaker on the subscription. New deliveries wait out a cooldown (1 → 30 min) without burning an attempt. A success closes it. * After 10 failed attempts the subscription is auto-disabled (`disabled_reason=retry_exhausted`) and a `message.attempt.exhausted` event fires. New events enter the delivery queue on a one-minute fanout schedule. Provider callbacks can arrive after an on-chain transaction confirms. The event timestamp shows when Meow recorded the event, not the blockchain confirmation time. To recover: `PATCH /webhooks/subscriptions/{id}` with `{"is_enabled": true}`, or replay a single delivery with `POST /webhooks/deliveries/{id}/redrive`. ## 7. Operate Sends `webhook.test` to one subscription. Good for verifying a new receiver without waiting for real activity. Every attempt with HTTP status and response body excerpt. Resets the counter and re-queues. Works even after `failed_permanent`. `PATCH` with `rotate_secret: true`. Both old and new secrets sign for 7 days; pick whichever your code recognizes. ## 8. Security checklist Constant-time compare. `==` leaks the secret. Otherwise a leaked payload can be replayed forever. Same event can arrive twice (retries, redrives). `webhook-id` doesn't change. Advance the highest `sequence` per resource with an atomic conditional write so a stale delivery can't overwrite newer state. See [Handle out-of-order deliveries](#5-handle-out-of-order-deliveries). ## See also * [Event catalog](/api-reference/webhooks/events): payload shape per event. * [Standard Webhooks](https://www.standardwebhooks.com/): the wire format. * [`POST /webhooks/subscriptions`](/api-reference/webhooks/create-subscription): full subscription contract. # Meow API for business banking, transfers, and invoicing Source: https://developer.meow.com/introduction Build with the Meow API to access bank accounts, send ACH, wire, book, and USDC transfers, manage contacts, and issue invoices for your business. Meow API hero image Comprehensive financial services and billing capabilities for businesses. [Learn more about building integrations with Meow](https://meow.com) ## Quickstart Guides Need help? [Submit a support request](mailto:support@meow.com) and our team will follow up. # Connect AI assistants to Meow with the MCP server Source: https://developer.meow.com/mcp-server Connect Claude, Cursor, ChatGPT, and other AI assistants to your Meow account over MCP. Read balances, transactions, and cards, and draft payments you approve. The Meow MCP server lets AI assistants work with your Meow account over the [Model Context Protocol](https://modelcontextprotocol.io). An assistant can list entities and accounts, check balances, review transactions, look up contacts, manage invoices and bills, issue virtual cards, and draft payments for you to approve. **Payments are always drafted, never sent automatically.** When an assistant creates a transfer through MCP, Meow records it as a draft in a **pending-approval** state. No money moves until a human approves it on the [Meow dashboard](https://app.meow.com/signin). See [Security & trust](/mcp/security#human-in-the-loop-approvals). ## What you can do Set up Claude, Cursor, VS Code, ChatGPT, and other MCP clients. OAuth sign-in, the API-key surface, and what each scope unlocks. Every tool the server exposes, grouped by resource. The human-in-the-loop model, least privilege, and auditability. ## Two ways to connect The Meow MCP server has two surfaces. Most users want OAuth. | | OAuth surface | CLI / API-key surface | | ------------------ | -------------------------------------------------------------------------------------- | --------------------------------------------------- | | **Server URL** | `https://mcp.meow.com` | `https://mcp.meow.com/cli` | | **Best for** | Interactive assistants (Claude.ai, ChatGPT, Cursor) | Coding agents and automation; agent-led signup | | **Authentication** | Sign in once with your Meow login (OAuth) | Bearer API key, or guided signup for new accounts | | **Access** | All entities your login can reach | The entity your API key is scoped to | | **Permissions** | Chosen at connect time with a [scope picker](/mcp/authentication#choose-what-to-share) | Defined by your [API key's scopes](/authentication) | See [Connect a client](/mcp/connect) for copy-paste setup for each client. ## Capabilities at a glance | Area | What the assistant can do | Access | | ------------------- | --------------------------------------------------------------------------------------- | ------------------- | | Entities & accounts | List business entities, accounts, balances, and payment networks | Read | | Transactions | List and look up transactions, ACH, wire, and USDC transfers | Read | | Contacts | List and look up saved payment contacts | Read | | Cards | List cards and activity, view spending insights, **issue** virtual cards, freeze/revoke | Read + action | | Invoices & billing | List and create products, customers, and invoices | Read + action | | Bills | List and look up payables | Read | | Transfers | **Draft** ACH, scheduled ACH, wire, book, and crypto payments for human approval | Action (draft only) | | Account signup | Open a Meow account and upload documents through the assistant | Action | Read tools are always available. Action tools require the matching [scope](/mcp/authentication#scopes) and are governed by your role, so an assistant can never do more than you can. ## Get connected in two minutes Choose your assistant on the [Connect a client](/mcp/connect) page. Use the install button or paste the config. For Claude Code: `claude mcp add --transport http meow https://mcp.meow.com` Sign in with OAuth (or add your API key), then choose which permissions to grant. Ask "What are my account balances?" See more [example prompts](/mcp/tools#example-prompts). ## Prerequisites * A [Meow account](https://app.meow.com/signin) (or use [agent-led signup](/mcp/connect#agent-led-signup) to create one) * A supported [MCP client](/mcp/connect) * Network egress enabled in your AI agent. In [Claude.ai](https://claude.ai/settings/capabilities), go to **Settings → Capabilities**. In Claude Code, toggle **Allow network egress** to **All domains**. See [Troubleshooting](/mcp/troubleshooting) if tools don't appear. # Meow MCP authentication, OAuth flow, and permission scopes Source: https://developer.meow.com/mcp/authentication How the Meow MCP server authenticates AI assistants: OAuth 2.1 with PKCE, the post-login scope picker, API keys, and what each meow.* scope grants. The Meow MCP server has two authentication surfaces. Use OAuth for interactive assistants and API keys for the CLI surface. Either way, an assistant can never do more than your role and granted permissions allow. ## OAuth surface (`mcp.meow.com`) Sign in once with your Meow login. The server runs OAuth 2.1 with PKCE and sits in front of Meow's identity provider as an authorization proxy, so your MCP client never talks to that provider directly. ### The connection flow Your MCP client discovers the server's metadata, registers, and opens the authorization URL with PKCE and the `meow.*` scopes it wants. A Meow consent screen shows which client is connecting. Approve it to continue to sign-in. Authenticate with your Meow credentials or Google. This step confirms who you are; what you can do is decided separately, in the next step. A scope picker appears after sign-in, so it can hide permissions your role could never use. Pick the scopes to grant. The client exchanges its authorization code, together with the PKCE verifier, for an access token and starts calling tools. ### Choose what to share The scope picker appears after you sign in. `meow.read` is always granted. The rest are opt-in, and each one only shows up if your role can actually use it. | Scope | Grants | Shown when | | ---------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------- | | `meow.read` | All read access: accounts, balances, transactions, payment networks, contacts, billing, bills, and cards | Always (locked on) | | `meow.transfers` | Draft ACH, scheduled ACH, wire, book, and crypto payments | Your role can initiate transfers | | `meow.cards` | Issue, update, freeze, and revoke virtual cards | Your role can manage cards | | `meow.billing` | Create and manage invoicing products, customers, and invoices | Your role can manage billing | Granting a write scope never bypasses your role. Permissions are re-checked per entity on every call, so someone who is an admin on one entity and a viewer on another sees the write option but can only use it where their role allows. ### What's enforced on every call Each tool call is validated on its own. A valid token alone is never enough. The server checks that: * the access token is genuine and was issued for this server; * your role allows the action on the target entity; * you granted the matching OAuth scope at connect time; * the entity is active; * the session has not been revoked; * any MFA requirement is satisfied. The permission that actually applies is the intersection of your role and the scopes you granted, so the narrower of the two always wins. ### Revoking access You can disconnect an assistant at any time from the [Meow dashboard](https://app.meow.com/signin). Revocation is immediate: existing tokens stop working on their next call, and reconnecting starts a fresh OAuth flow. ## CLI / API-key surface (`mcp.meow.com/cli`) The CLI surface authenticates with a Meow API key sent as a bearer token and scoped to a single entity. Use it for coding agents, automation, and [agent-led signup](/mcp/connect#agent-led-signup). * Create and scope keys from the [Meow dashboard](https://www.meow.com/dashboard). See [API key authentication](/authentication). * Use the narrowest scopes the assistant needs. A read-only key can't draft transfers or issue cards. * Never commit a config file that contains an API key. Keep it in a secrets manager or environment variable. This surface exposes the same read tools as OAuth, plus card, transfer (draft), and billing actions when your key carries those scopes. ## Scopes OAuth `meow.*` scopes map onto the API key scopes you already know from the [REST API](/authentication): | OAuth scope | Underlying API key scopes | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `meow.read` | `accounts:read`, `accounts:balances`, `accounts:transactions`, `accounts:payment_networks`, `contacts:read`, `billing:*:read`, `billpay:read`, `cards:read` | | `meow.transfers` | `transfers:ach:write`, `transfers:wire:write`, `transfers:book:write`, `transfers:crypto:write` | | `meow.cards` | `cards:write` | | `meow.billing` | `billing:invoices:write`, `billing:customers:write`, `billing:products:write` | ## Next steps See which scope each tool requires. The human-in-the-loop model and auditability. # Connect Claude, Cursor, VS Code, and ChatGPT to Meow MCP Source: https://developer.meow.com/mcp/connect Step-by-step setup for connecting Claude.ai, Claude Code, Cursor, VS Code, ChatGPT, Goose, and other MCP clients to the Meow MCP server with OAuth or API keys. The Meow MCP server speaks HTTP, the recommended transport for remote MCP servers. Pick your client below for copy-paste configuration. For how authentication and permissions work once you connect, see [Authentication & scopes](/mcp/authentication). ## Choose a server URL | Server URL | Use it for | | -------------------------- | ------------------------------------------------------------------------------------------------------------- | | `https://mcp.meow.com` | **OAuth.** Interactive assistants where you sign in with your Meow login. | | `https://mcp.meow.com/cli` | **CLI / API key.** Coding agents and automation, plus [agent-led signup](#agent-led-signup) for new accounts. | Most people want OAuth: use `https://mcp.meow.com` and sign in with your Meow account. Reach for `https://mcp.meow.com/cli` when you're building a coding agent or automation, or when you don't have an account yet and want [agent-led signup](#agent-led-signup). ## Client setup To open Cursor and add the Meow MCP automatically, click install. Or add the following to `~/.cursor/mcp.json`. See the [Cursor documentation](https://cursor.com/docs/mcp). Install in Cursor ```json theme={null} { "mcpServers": { "meow": { "url": "https://mcp.meow.com" } } } ``` Building a coding agent, or don't have a Meow account yet? Use the CLI surface `https://mcp.meow.com/cli` instead. ```json theme={null} { "mcpServers": { "meow": { "url": "https://mcp.meow.com/cli" } } } ``` To open VS Code and add the Meow MCP automatically, click install. Or add the following to `.vscode/mcp.json` in your workspace. See the [VS Code documentation](https://code.visualstudio.com/docs/copilot/customization/mcp-servers). Install in VS Code ```json theme={null} { "servers": { "meow": { "type": "http", "url": "https://mcp.meow.com" } } } ``` Building a coding agent, or don't have a Meow account yet? Use the CLI surface `https://mcp.meow.com/cli` instead. ```json theme={null} { "servers": { "meow": { "type": "http", "url": "https://mcp.meow.com/cli" } } } ``` Add the server with the CLI. See the [Claude Code documentation](https://code.claude.com/docs/en/mcp#configure-mcp-servers). ```bash theme={null} claude mcp add --transport http meow https://mcp.meow.com ``` Then authenticate from inside Claude Code: ```bash theme={null} claude /mcp ``` When the server needs authentication, Claude Code marks it as requiring sign-in (an HTTP 401). Run `/mcp` to complete the OAuth flow in your browser; tokens are stored securely and refreshed automatically. Building a coding agent, or don't have a Meow account yet? Use the CLI surface instead: ```bash theme={null} claude mcp add --transport http meow https://mcp.meow.com/cli ``` In [Claude.ai](https://claude.ai/settings/connectors), go to **Settings → Connectors → Add custom connector** and enter the server URL: * Server URL: `https://mcp.meow.com` * Connection: **OAuth**. You'll be redirected to sign in to Meow and [choose permissions](/mcp/authentication#choose-what-to-share). Make sure network egress is enabled under **Settings → Capabilities**. You can enable MCP connectors on ChatGPT with a Pro, Plus, Business, Enterprise, or Education account. Follow the [OpenAI documentation](https://developers.openai.com/api/docs/guides/developer-mode), using: * Server URL: `https://mcp.meow.com` * Connection: **OAuth** Building a coding agent, or don't have a Meow account yet? Use `https://mcp.meow.com/cli` instead. MCP is an open protocol supported by many clients (Goose, Windsurf, Cline, and more). Consult your client's documentation, and use the server URL `https://mcp.meow.com` with **OAuth** as the connection mechanism. ```json theme={null} { "meow": { "url": "https://mcp.meow.com" } } ``` Building a coding agent, or don't have a Meow account yet? Use `https://mcp.meow.com/cli` instead. ```json theme={null} { "meow": { "url": "https://mcp.meow.com/cli" } } ``` ## Client support matrix | Client | Recommended URL | Auth | Install method | | ------------- | --------------- | ---------------- | ---------------------- | | Cursor | `mcp.meow.com` | OAuth | Deeplink or `mcp.json` | | VS Code | `mcp.meow.com` | OAuth | Deeplink or `mcp.json` | | Claude Code | `mcp.meow.com` | OAuth via `/mcp` | `claude mcp add` | | Claude.ai | `mcp.meow.com` | OAuth | Custom connector | | ChatGPT | `mcp.meow.com` | OAuth | Custom connector | | Goose / other | `mcp.meow.com` | OAuth | Client config | The CLI surface (`mcp.meow.com/cli`) is the alternative for coding agents, automation, and [agent-led signup](#agent-led-signup). ## Agent-led signup Don't have a Meow account yet? Connect to `https://mcp.meow.com/cli` and ask your assistant to open one. The assistant walks you through the entire flow: * Call the `start` tool first. It returns step-by-step instructions, and each response tells the assistant what to do next. * Add, update, or remove owners and signers from the conversation. * Upload required documents through the assistant. After each tool call, the assistant follows the `step_instructions.directive` field in the response. This keeps the guided signup on track without you leaving the chat. ## Next steps Understand the OAuth flow and what each permission grants. See every tool and example prompts to try. # Meow MCP security: human approval and least-privilege scopes Source: https://developer.meow.com/mcp/security How the Meow MCP server protects your money: payments drafted for human approval, least-privilege scopes, audited tool calls, and prompt-injection containment. The Meow MCP server gives AI assistants access to financial data and actions. An assistant can read what your role permits and draft payments you must approve. It can't do anything beyond that. ## Human-in-the-loop approvals **MCP transfer tools never move money.** When an assistant calls `create_ach_transfer`, `create_scheduled_ach_transfer`, `create_wire_transfer`, `create_book_transfer`, or `create_crypto_transaction`, Meow records the payment as a draft in a pending-approval state. Funds move only after a person approves the draft on the [Meow dashboard](https://app.meow.com/signin). The server enforces this, not the client: * Every MCP-initiated transfer is forced into `pending_approval`, no matter which assistant or surface created it. * The tool returns an approval reference. The assistant can check status with `get_approval`, but it can't approve on your behalf. * Approval happens on the dashboard, by someone with the authority to approve it. Two independent layers of human control apply. The first is this server-side draft model, built into Meow. The second is client-side: because every action tool is annotated as destructive (see [Tool annotations](#tool-annotations)), MCP clients can prompt you to confirm a call before it runs. In Claude, team and enterprise admins can set each connector permission to **Always allow**, **Needs approval**, or **Blocked**. ## Least privilege Access is scoped as narrowly as possible at every layer. Scopes are granted per capability. Because `meow.read`, `meow.transfers`, `meow.cards`, and `meow.billing` are independent, you can connect an assistant for read-only insights and never grant write access at all. See [scopes](/mcp/authentication#scopes). The scope picker only offers optional write scopes if your role could exercise them somewhere, and role permissions cap every call regardless of what you grant. On the [CLI surface](/mcp/authentication), the same idea applies to API keys: give the assistant a key with only the scopes it needs. ## What's checked on every tool call The server re-validates each call independently. A valid token on its own is not enough. It confirms the token's signature, expiry, and audience (the token must have been issued for this server), your role's permissions on the target entity, the OAuth scopes you granted at connect time, the entity's lifecycle, the session's revocation status, and any multi-factor requirement. ## Tool annotations Every tool carries an MCP annotation so clients can render the right warning UI. Read tools are marked `readOnlyHint: true` and `idempotentHint: true`; action tools are marked `destructiveHint: true`. That lets a client confirm before any state-changing tool runs, and it lets directory reviewers and admins see at a glance what each tool does. ## Prompt-injection awareness Agentic systems that read untrusted content (web pages, emails, documents) can be steered by malicious instructions hidden in that content. A few things contain the risk here: * Even a successful injection can't move money, because transfers are drafts that wait for human approval on the dashboard. * A read-only connection can't be coerced into a write, so grant only the scopes the task needs. * Running the Meow MCP alongside other MCP servers in one session widens the attack surface, so be cautious about it and review the actions an assistant proposes before approving them. ## Auditability Every tool invocation is logged with enough context to reconstruct it: the authenticated identity, the tool name, the parameters (with sensitive values redacted), the timestamp, the result status, and the duration. Each response also carries a request ID you can share with [support](mailto:support@meow.com). OAuth sessions are tracked, so you can list the assistants you've connected and [revoke](/mcp/authentication#revoking-access) any of them from the dashboard. Revocation takes effect immediately. ## Data handling * The MCP server reaches the same financial data as the [REST API](/api-reference/overview), scoped to your permissions. * Prefer OAuth over long-lived API keys for interactive use, and keep API keys out of any file you commit. ## Next steps The OAuth flow and what each scope grants. Resolve connection and permission errors. # Meow MCP tool reference: accounts, cards, transfers, billing Source: https://developer.meow.com/mcp/tools Every tool the Meow MCP server exposes, grouped by resource: accounts, balances, transactions, contacts, cards, transfers, billing, with required scopes. This page lists every tool the Meow MCP server exposes. Tools are grouped by resource. For each tool you'll see the **scope** it requires and whether it is **read** (returns data, no side effects) or an **action** (changes state). Every tool is annotated for MCP clients with a `readOnlyHint` or `destructiveHint`, so clients can show a confirmation prompt before any action runs. Read tools are marked read-only and idempotent; action tools are marked destructive. See [Security & trust](/mcp/security). Most tools are available on **both** the OAuth and CLI surfaces. The few exceptions are noted in the Surface column. ## Reading the tables | Column | Meaning | | ----------- | ---------------------------------------------------------------------------------------------------------------- | | **Tool** | The tool name (operation ID) the assistant calls | | **Scope** | The [scope](/mcp/authentication#scopes) required: `meow.read`, `meow.transfers`, `meow.cards`, or `meow.billing` | | **Type** | **Read** or **Action** | | **Surface** | Both, OAuth only, or CLI only | ## Entities & session Start here. List your entities, then pass the returned `entity_id` to other tools. | Tool | Scope | Type | Surface | | ------------------ | ----------- | ---- | ------- | | `list_my_entities` | `meow.read` | Read | OAuth | | `get_my_entity` | `meow.read` | Read | CLI | | `get_session_info` | `meow.read` | Read | Both | `get_session_info` returns your current role, entity, and granted scopes. An assistant can call it to check what it's allowed to do before it attempts an action. ## Accounts & balances | Tool | Scope | Type | Surface | | ------------------------------- | ----------- | ---- | ------- | | `list_bank_accounts` | `meow.read` | Read | Both | | `get_bank_account` | `meow.read` | Read | Both | | `get_account_balances` | `meow.read` | Read | Both | | `list_account_payment_networks` | `meow.read` | Read | Both | ## Transactions | Tool | Scope | Type | Surface | | ------------------------------ | ----------- | ---- | ------- | | `list_account_transactions` | `meow.read` | Read | Both | | `get_usdc_transaction` | `meow.read` | Read | Both | | `get_ach_transfer` | `meow.read` | Read | Both | | `get_wire_transfer` | `meow.read` | Read | Both | | `list_scheduled_ach_transfers` | `meow.read` | Read | Both | ## Contacts | Tool | Scope | Type | Surface | | --------------- | ----------- | ---- | ------- | | `list_contacts` | `meow.read` | Read | Both | | `get_contact` | `meow.read` | Read | Both | ## Cards Read tools require `meow.read`; card actions require `meow.cards`. Cards issued through MCP are virtual cards scoped to a single merchant and spend limit. See [Issuing cards](#issuing-cards) below. | Tool | Scope | Type | Surface | | ------------------------ | ------------ | ------ | ------- | | `list_cards` | `meow.read` | Read | Both | | `list_card_transactions` | `meow.read` | Read | Both | | `get_card_insights` | `meow.read` | Read | Both | | `get_card` | `meow.read` | Read | Both | | `get_card_details` | `meow.read` | Read | Both | | `create_card` | `meow.cards` | Action | Both | | `update_card` | `meow.cards` | Action | OAuth | | `revoke_card` | `meow.cards` | Action | Both | ### Issuing cards `create_card` issues a **virtual card scoped to a single merchant and spend limit**. By default the card is single-use and auto-cancels after the first authorization; set `single_use: false` for a multi-use card that stays active until expiry or explicit revoke. `update_card` is available on the OAuth surface only. On the CLI surface, revoke the card and issue a new one instead. ## Transfers (draft only) **These tools never move money.** Each one creates a payment as a **draft in a pending-approval state**. A human must approve it on the [Meow dashboard](https://app.meow.com/signin) before any funds move. The tool returns an approval reference you can check with `get_approval`. See [Human-in-the-loop approvals](/mcp/security#human-in-the-loop-approvals). All transfer tools require the `meow.transfers` scope and are available on both surfaces. | Tool | Scope | Type | Drafts | | ------------------------------- | ---------------- | -------------- | -------------------------------------------- | | `create_ach_transfer` | `meow.transfers` | Action (draft) | ACH transfer to a counterparty | | `create_scheduled_ach_transfer` | `meow.transfers` | Action (draft) | Scheduled ACH transfer | | `create_wire_transfer` | `meow.transfers` | Action (draft) | Wire transfer | | `create_book_transfer` | `meow.transfers` | Action (draft) | Internal book transfer between your accounts | | `create_crypto_transaction` | `meow.transfers` | Action (draft) | USDC transfer to a contact | | `get_approval` | `meow.read` | Read | Checks a draft's approval status | ## Bills | Tool | Scope | Type | Surface | | ------------ | ----------- | ---- | ------- | | `list_bills` | `meow.read` | Read | Both | | `get_bill` | `meow.read` | Read | Both | ## Invoices & billing Read tools require `meow.read`; create tools require `meow.billing`. | Tool | Scope | Type | Surface | | --------------------------- | -------------- | ------ | ------- | | `list_products` | `meow.read` | Read | Both | | `get_product` | `meow.read` | Read | Both | | `create_product` | `meow.billing` | Action | OAuth | | `list_invoicing_customers` | `meow.read` | Read | Both | | `get_invoicing_customer` | `meow.read` | Read | Both | | `create_invoicing_customer` | `meow.billing` | Action | OAuth | | `list_payment_method_types` | `meow.read` | Read | Both | | `list_invoices` | `meow.read` | Read | Both | | `get_invoice` | `meow.read` | Read | Both | | `list_invoice_line_items` | `meow.read` | Read | Both | | `get_invoice_line_item` | `meow.read` | Read | Both | | `create_invoice` | `meow.billing` | Action | OAuth | | `list_collection_accounts` | `meow.read` | Read | Both | Creating an invoice is a request for payment, not money movement. It doesn't move funds and doesn't need dashboard approval. ## Tax forms, security policies & utilities | Tool | Scope | Type | Surface | | ------------------------- | ----------- | ---- | ------- | | `search_tax_forms` | `meow.read` | Read | Both | | `get_tax_form` | `meow.read` | Read | Both | | `list_security_policies` | `meow.read` | Read | Both | | `validate_routing_number` | `meow.read` | Read | Both | ## Account signup (CLI surface) On the CLI surface, an assistant can open a new Meow account end-to-end. The flow is guided: call `start` first, then follow the `step_instructions.directive` returned by each response. See [Agent-led signup](/mcp/connect#agent-led-signup). ## Example prompts Once connected, try asking your assistant: * "What entities do I have access to?" * "What are my account balances?" * "Show me my recent transactions for the operating account." * "List my contacts." * "Show me all pending bills." * "Issue a single-use virtual card for \$200 at AWS." * "Draft an ACH transfer of \$1,500 to Acme Corp." *(creates a draft you approve on the dashboard)* * "What's the approval status of that transfer?" ## Next steps How approvals, scopes, and auditing protect your account. Fixes for connection, auth, and tool errors. # Troubleshoot the Meow MCP server: connection and auth errors Source: https://developer.meow.com/mcp/troubleshooting Fix common Meow MCP issues: tools not appearing, network egress, OAuth re-authentication, scope and permission errors, drafted transfers, and getting support. Solutions to the most common issues connecting to and using the Meow MCP server. If you're stuck, [contact support](mailto:support@meow.com) with the request ID from the error response. ## Tools don't appear after connecting Most clients block outbound network access by default. Enable it: * **Claude.ai**: Settings → Capabilities, allow network egress. * **Claude Code**: Settings → Capabilities → **Allow network egress** → **All domains**. Reconnect the server after enabling egress. If the client reports the server as unauthenticated (an HTTP 401 or 403), complete the sign-in flow. In Claude Code, run `/mcp` and follow the browser prompt. See [Authentication](/mcp/authentication). The Meow MCP server uses **HTTP** transport. Confirm your config uses `"type": "http"` where required and one of `https://mcp.meow.com` (OAuth) or `https://mcp.meow.com/cli` (CLI). See [Connect a client](/mcp/connect). ## Authentication & permission errors The effective permission is the intersection of your role and the scopes you granted. If an action fails: * Check `get_session_info` to see your role and granted scopes. * Reconnect and grant the needed scope (for example, `meow.transfers`) in the [scope picker](/mcp/authentication#choose-what-to-share). * Confirm your role on the target entity actually allows the action. Granting a scope never exceeds your role. The scope picker hides scopes your role can't use on any entity. If `meow.transfers`, `meow.cards`, or `meow.billing` doesn't appear, your role lacks that permission. Ask an admin to adjust it. The session may have been [revoked](/mcp/authentication#revoking-access) from the dashboard, or the token expired. Reconnect to start a fresh OAuth flow. Multi-factor requirements are also enforced per call. Confirm the key is active and carries the scopes the assistant needs. Create and scope keys from the [dashboard](https://www.meow.com/dashboard). See [API key authentication](/authentication). ## Payments & cards That's expected. MCP transfers are drafts that sit in a pending-approval state until a human approves them on the [Meow dashboard](https://app.meow.com/signin). Check status with `get_approval`. See [Human-in-the-loop approvals](/mcp/security#human-in-the-loop-approvals). `update_card` is exposed on the **OAuth surface** only (`mcp.meow.com`). On the CLI surface, revoke and reissue instead. ## Error format & getting support MCP tool errors mirror the [REST API error format](/errors): a JSON body with `code`, `message`, and `debug_message`. Every response also carries a request ID. When contacting [support@meow.com](mailto:support@meow.com), include: * The request ID from the error response * The tool name and surface (OAuth or CLI) * What you asked the assistant to do ## Next steps Every tool and the scope it requires. New MCP capabilities over time.