
PayPal gives an AI agent two ways into a merchant's business account: an official MCP server, hosted and local, built on its open-source Agent Toolkit, and the REST API every checkout integration already uses. They overlap on invoices, orders, and refunds. They diverge on payouts, authorizations, dispute handling, events, and most of all on the credential model, because nearly every path into PayPal ends at a client ID and secret that can move a merchant's money. This guide compares what each path lets an agent do and how to hold those credentials for many merchants in production.
Your agent needs to work inside your customers' PayPal business accounts: chase unpaid invoices, refund a damaged order, answer a buyer's dispute before the seller response deadline, add tracking numbers so payouts aren't held, or pay out a batch of contractors on Friday. PayPal ships an official Model Context Protocol (MCP) server, hosted and local, built on its open-source Agent Toolkit. PayPal also has the REST API every checkout integration already uses. They overlap on invoices, orders, and refunds. They diverge on payouts, authorizations, dispute handling, events, and, most of all, on the credential model, because nearly every path into PayPal ends at a client ID and secret that can move a merchant's money. Here's how to pick.
PayPal announced its MCP server on April 2, 2025 with invoicing as the first feature, offered both as a local server and as a remote server that "connects with a PayPal login." A June 2, 2025 update re-architected the remote server on the Agent Toolkit so it serves all the toolkit's tools, and restricted tool visibility so a client sees only the tools its token permits.
The MCP server docs list two remote endpoints, https://mcp.paypal.com for production and https://mcp.sandbox.paypal.com for sandbox, each with /sse and /http (Streamable HTTP) paths. Auth on the remote server is either a browser redirect to the PayPal login page, where you authorize the client, or an Authorization: Bearer header built from client ID and secret from the PayPal Developer Dashboard. The local server runs with npx -y @paypal/mcp --tools=all on Node.js 18 or later and takes PAYPAL_ACCESS_TOKEN plus PAYPAL_ENVIRONMENT set to SANDBOX or PRODUCTION. PayPal doesn't label either server GA or beta, and its docs warn that MCP and MCP clients "can come with performance issues or other challenges initially."
The Agent Toolkit README lists 43 tools, including create_invoice, send_invoice_reminder, create_recurring_series, create_order, pay_order, create_refund, list_disputes, accept_dispute_claim, create_shipment_tracking, create_subscription, cancel_subscription, list_transactions, and get_merchant_insights. PayPal's agent tools catalog shows a smaller 30-tool set plus three remote-only commerce tools, search_product, create_cart, and checkout_cart, which are limited to gift cards and need an x-feature-flags: commerce:true header. The catalog and the README don't match exactly, so check what your token actually returns from tools/list.
The PayPal REST API lives at https://api-m.paypal.com, with a sandbox at https://api-m.sandbox.paypal.com. It covers Orders v2, Payments v2 (authorizations, captures, refunds), Invoicing v2, Payouts, Disputes, Subscriptions and Catalog Products, Transaction Search, Balances, Add Tracking, Partner Referrals, and Webhooks Management. Authentication is OAuth 2.0 Client Credentials: you send the REST app's client ID and secret as HTTP Basic auth with grant_type=client_credentials to /v1/oauth2/token, and every API call carries the returned bearer token. Sandbox and live apps have separate credentials, created under Apps & Credentials in the developer dashboard.
The pattern is clear. PayPal's MCP tools follow what a merchant does in a chat: draft an invoice, look something up, refund an order, concede a dispute. The API covers the money operations a back office runs without a chat window: holds, payouts, balances, dispute negotiation, and reactions to events.
Two things. The first is invoicing depth. PayPal has invested most of the toolkit in invoices, and tools like create_recurring_series, create_conditional_rules_for_invoice, and cancel_invoice_auto_reminder let a merchant say "bill Globex $4,000 on the first of every month, with 2% off if they pay in ten days" without your team writing a schema for PayPal's nested invoice detail, amount breakdown, and payment term objects. Writing the schema is the hard part. Not the API call.
The second is aggregation. get_merchant_insights returns sales or order metrics over a date range and interval in one call. On the raw API, "how did Q3 compare to Q2" means paging GET /v1/reporting/transactions in 31-day windows, at most 500 rows a page, filtering event codes, and summing gross, fee, and net yourself, with each page landing in the model's context.
The tradeoff is that PayPal owns the surface. The README and the developer catalog already disagree on the count and on names (list_product versus list_products, list_transaction versus list_transactions), and which tools a client sees depends on the token. An agent tuned against today's names can change behavior without a release on your side.
On the MCP path, auth depends on which server you pick. The remote server either sends the user through a PayPal login and authorization page, which gives a person-level session inside the MCP client, or takes a bearer header built from REST app client credentials. The local server takes a raw access token in PAYPAL_ACCESS_TOKEN, and the toolkit README says that token is valid for three to eight hours in sandbox and eight hours in production, with a recommendation to "implement token refresh logic before expiration." A local MCP config with a pasted token stops working the same day.
On the direct API path, there is one grant for your own account: Client Credentials. PayPal's sample token responses carry expires_in values of 31,668 and 32,400 seconds, roughly eight to nine hours, and a scope string listing what the app may call: invoicing, disputes, payments, refunds, payouts, subscriptions, vault, and webhooks among them. There is no refresh token; you request a new access token with the same credentials. PayPal's rate limiting page tells you not to generate a new token for each transaction and to cache it instead.
The identity is the catch. A Client Credentials token acts as the merchant account that owns the REST app. Every refund, payout, and dispute offer is attributed to that app, not to the employee who asked the agent for it. PayPal has no per-user scope inside a business account on this path, so the line between what a support agent may refund and what a finance lead may pay out is yours to draw.
Acting for other merchants adds two PayPal-specific shapes. In a first-party integration, you onboard each seller through POST /v2/customer/partner-referrals, exchange the returned authCode and sharedId for the seller's token, and then retrieve the seller's client_id and client_secret from the partner's merchant-integrations/credentials endpoint under /v1/customer/partners/. Partner Referrals requires approved-partner status. The other shape is a single platform credential plus a PayPal-Auth-Assertion header, an unsigned JWT carrying your client_id as iss and the merchant's payer_id, which "requires explicit consent from each merchant you represent" and must travel with a PayPal-Partner-Attribution-Id header. Both shapes end with you holding a credential that can move each merchant's money.
In a multi-tenant B2B agent, MCP gives you a credential per user and the direct API gives you a credential per user. In neither case does the path itself solve storage, rotation, or revocation; those are infrastructure problems regardless of which path you choose.
PayPal sharpens the stakes. A leaked OAuth refresh token for a calendar is a privacy incident. A leaked PayPal client secret with the payouts feature enabled is a funds incident, and it stays valid until someone rotates it in the developer dashboard. Your app has to bind each set of REST app credentials to exactly one customer, keep sandbox and live credentials in separate environments, and make sure no code path lets one tenant's agent run with another tenant's secret.
Recommended reading: access control for multi-tenant AI agents
On the MCP path, PayPal hosts the remote server, owns the tool schemas, and filters tools by token. You own each merchant's session or token inside an MCP client you may not control, and you own the gaps. MCP tools only run when someone starts a conversation, so nothing happens when a dispute opens or a payment is reversed. On the local server you also own the token: something has to mint a new PAYPAL_ACCESS_TOKEN every eight hours and restart or reconfigure the client.
On the API path, you own schemas, pagination, retries, and the token lifecycle. PayPal states that "we do not publish a rate limiting policy," returns 429 with RATE_LIMIT_REACHED when it throttles, and asks integrations not to poll for status. Transaction search has its own limits: date ranges of at most 31 days, history back three years, and new transactions that can take up to three hours to appear. Money-moving POST calls should carry a PayPal-Request-Id, which PayPal keeps for up to 45 days so a retry returns the original result instead of refunding twice; the Scalekit tools expose it as request_id on paypal_capture_refund, paypal_payout_create, paypal_order_create, and paypal_authorization_void.
Webhooks replace polling, with rules. Subscriptions belong to a REST app, so in a per-merchant-app model each merchant's app needs its own subscription and its own webhook_id. Your listener must return a 2xx, or PayPal retries up to 25 times over three days. You verify each delivery either by posting it back to /v1/notifications/verify-webhook-signature or by checking the paypal-transmission-sig header yourself against PayPal's certificate, over a string built from the transmission ID, transmission time, your webhook_id, and the CRC32 of the raw body. Either way you need the raw, unparsed body.
Use PayPal MCP when:
Use the PayPal API when:
Both paths hand you a PayPal credential per merchant. Neither hands you a vault. A multi-tenant agent serving 120 merchants holds 120 REST app client ID and secret pairs on the API path, each minting a token that expires roughly every eight hours, each able to refund, invoice, and in many cases pay out. Behind them may sit your own platform app, whose secret anchors every PayPal-Auth-Assertion you send. Every secret must be stored encrypted, kept out of logs and LLM context, exchanged for a cached token rather than a fresh one per call, rotated when a merchant suspects exposure, and deleted when a merchant offboards.
The MCP path changes the shape, not the problem. Login-based sessions live in each merchant's MCP client, the local server needs a fresh access token pasted in every eight hours, and the bearer option on the remote server is the same client credential again, now sitting in a desktop config file. The tempting shortcut is one platform-level credential for everyone. That gives you one secret to protect and a blast radius the size of your entire merchant base.
Scalekit's paypal connector holds each merchant's REST app credentials in the token vault as a connected account and handles authentication on every call, so the MCP vs API decision doesn't change your auth infrastructure. Your agent passes an identifier; credentials never touch the agent runtime or the LLM context.
Recommended reading: OAuth vs API keys for AI agents
Scalekit's paypal connector is an API-path connector built by Scalekit with 63 tools: 22 read, 41 write, and 0 destructive. It covers orders, authorizations, captures, refunds, invoices and invoice templates, disputes, payouts, catalog products, billing plans, subscriptions, shipment trackers, balances, and transaction search. It authenticates as a service account using a PayPal REST app's client credentials, so there is no consent screen and no refresh token to babysit: the connected account holds the app's client ID and secret, and Scalekit handles authentication for every call.
In the Scalekit dashboard, go to AgentKit > Connections and create a PayPal connection. The name you give the connection is the connection_name your code passes, and the string must match the dashboard exactly. This is the most common integration error. Copy SCALEKIT_ENVIRONMENT_URL, SCALEKIT_CLIENT_ID, and SCALEKIT_CLIENT_SECRET from Developers > API Credentials into .env.
Each merchant needs a PayPal REST app. In the PayPal developer dashboard under Apps & Credentials, the merchant creates an app and copies its Client ID and Secret. Sandbox and live apps are separate; keep sandbox credentials in a Scalekit development environment and live credentials in production. Enable only the features the agent needs on the app, because the token PayPal issues carries every scope the app has. The connector docs list each tool's inputs.
get_or_create_connected_account returns the connected account for an identifier. If it is not ACTIVE, get_authorization_link returns a Scalekit-hosted link; for a credential-based connector like PayPal it collects the credentials the connection requires instead of opening an OAuth consent screen. The merchant's PayPal admin enters the REST app credentials once, Scalekit stores them in the token vault, and the agent never sees them. The identifier is your customer's ID, because the credential covers the whole PayPal business account.
This is not a catalog load. The agent asks for the tools this connected account is authorized to call, then narrows them to the six a reconciliation agent needs. Loading all 63 would put paypal_payout_create, paypal_capture_refund, and paypal_subscription_cancel in front of a model whose job is to explain where the money went. One note on labels: paypal_invoices_search is classed as a write tool because PayPal's invoice search is a POST, but it changes nothing. None of the six below moves money.
The loop runs until the model stops requesting tools. Every call executes with this merchant's PayPal credentials, so the system prompt pins the agent to reading and to PayPal's 31-day search window.
For pipelines, skip the model. execute_tool runs one tool with a fixed input and returns data plus an execution_id you can log. This handler runs when your webhook endpoint receives a CUSTOMER.DISPUTE.CREATED event. It verifies the PayPal signature against PayPal's certificate, reads the dispute, and if the dispute is still in the inquiry stage, is $25 or less, and allows an offer, it proposes a full refund. Anything else goes to a person.
If the buyer accepts, PayPal issues the refund itself. The webhook subscription is not a connector tool. Create it once per merchant app with POST /v1/notifications/webhooks using that merchant's credentials, which Scalekit can make on the connected account's behalf through proxied API calls, and store the returned webhook_id per merchant. PayPal retries failed deliveries for up to three days, so deduplicate on the event id before calling the handler. The PayPal MCP server has no equivalent for either step.
Two other writes show where the API path earns its place. paypal_authorization_void releases a hold on a cancelled order so the buyer's funds come back without a refund cycle, and paypal_payout_create sends a batch to contractors by email, phone, or Venmo handle; PayPal rejects a sender_batch_id reused within 30 days, and request_id makes retries safe. Neither belongs in a model's default tool set; both fit a pipeline with a human approval step in front of them.
The paypal connector has 63 tools. At roughly 200 tokens each, that is about 12,600 tokens of tool definitions before the agent does any work. A model choosing among paypal_transactions_list, paypal_subscription_transactions_list, paypal_invoices_list, and paypal_invoices_search picks the wrong list more often than a model holding only the one it needs. Overreach matters more here than with most connectors: a dispute agent that also holds paypal_payout_create and paypal_capture_refund can send money on a bad inference. The fix is not better prompting. It is surface reduction. A Scalekit virtual MCP server declares exactly which tools an agent role can see, and session tokens keep each run bound to one merchant's connected accounts. Docs: set up a virtual MCP server.
One server definition serves every merchant. Before each run, check that the merchant's accounts are active and mint a short-lived session token scoped to them. Any MCP client that speaks Streamable HTTP can connect; this example uses langchain-mcp-adapters (pip install langchain-mcp-adapters) and passes the token as a Bearer header.
The agent sees five tools instead of 63. The endpoint is static; the identity is per merchant. Unlike PayPal's own MCP server, where tool visibility follows whatever the token permits, this endpoint is scoped by agent role: the dispute desk gets paypal_dispute_offer_make and never sees payouts, and a separate payouts role can hold paypal_payout_create without ever seeing disputes.
Every execute_tool call returns an execution_id, and every execution is attributed to a connected account, which maps to one of your merchants. When a merchant asks which agent run offered refunds on 14 disputes overnight or voided an authorization on Sunday, the answer is a lookup, not an investigation. PayPal keeps its own trail too: refunds, captures, and payouts show up in paypal_transactions_list, and custom_id and invoice_id on paypal_capture_refund let you stamp your own reference on each money movement.
Attribution is where Client Credentials bites. PayPal records every API call against the REST app, so from PayPal's side the support lead who asked for a refund and the pipeline that issued a payout look identical. Your execution log is the only place that records which of your users triggered which action. A shared platform credential across merchants weakens even that: every call traces back to one secret, and a leak exposes every merchant at once. Shared credentials work for a single user. They do not survive a second user.
Recommended reading: audit trails for agent auth
If your agent is a merchant's assistant in Claude or another MCP client, drafting and sending invoices, pulling sales insights, and handling the occasional refund with a person watching, PayPal's official MCP server is a good fit: it is hosted, it runs in sandbox and production, and its invoicing tools go deeper than most teams would build. If your agent runs unattended, reacts to dispute and payment webhooks, holds and releases authorizations, sends payouts, or reconciles balances across many merchants, build against the PayPal REST API, where you choose the operations and attach an idempotency key to every write. Either way, you hold a Client Credentials secret per merchant that can move that merchant's money, and that's what needs production-grade infrastructure.
Yes. PayPal runs a remote MCP server at https://mcp.paypal.com, with a sandbox at https://mcp.sandbox.paypal.com, over SSE and Streamable HTTP, plus a local server, @paypal/mcp, that reads a PAYPAL_ACCESS_TOKEN. Both expose the PayPal Agent Toolkit, whose README lists 43 tools for invoices, orders, refunds, disputes, shipment tracking, catalog, subscriptions, and reporting. PayPal's docs give it no GA or beta label.
There are no payout tools, no authorization void or reauthorize, no balances, no dispute offers or escalation, and no event triggers. Disputes are limited to listing, reading, and accepting a claim. Subscriptions can be created, shown, updated, and cancelled but not suspended or reactivated. The README and PayPal's developer catalog also disagree on tool count and names, so check what your token returns from tools/list.
A Client Credentials token acts as the merchant account that owns the REST app, not as a person. Every refund, payout, and dispute offer is attributed to that app, and PayPal has no per-user scope inside a business account on this path. Tokens live about eight to nine hours with no refresh token, and scopes come from the app's enabled features rather than the request.
PayPal sanctions two shapes. In a first-party integration you onboard each seller through Partner Referrals and retrieve that seller's own client_id and client_secret, which requires approved-partner status. Alternatively you use one platform credential plus a PayPal-Auth-Assertion header carrying the merchant's payer_id, which requires explicit consent from each merchant. Both end with you holding a credential that can move each merchant's money.
When it reacts to CUSTOMER.DISPUTE.CREATED webhooks and proposes refunds before the seller response deadline, when it sends scheduled payouts after confirming balances, when the checkout flow authorizes first and captures on shipment, or when it serves many merchants and needs credentials per merchant, webhooks per merchant, idempotency keys on every write, and reconciliation through transaction search.
The paypal connector exposes 63 tools over the PayPal REST API and authenticates as a service account with a REST app's client credentials, which each merchant's admin enters once in Scalekit's hosted form. Scalekit holds them in the token vault and handles authentication on every call, a virtual MCP server scopes tools by agent role so the dispute desk never sees payouts, and every execution returns an execution_id attributed to that merchant.
Browse the Scalekit PayPal connector and the PayPal connector docs with the full tool list. See Scalekit pricing, or talk to us.