For developers and their agents
One prompt, from an empty repository to a verified test payment.
Nalpay is built to be integrated without a person reading a manual. The whole API is one plain-text file an agent can read in a single fetch, the same capabilities are available as tools over MCP, and everything below works in test mode with no approval and no real money.
Connect your AI coding platformStep-by-step setup for Lovable, Replit, Claude, Cursor and more, with a copyable prompt and payment checks.The integration prompt
Copy this into Claude Code, Cursor, Codex or whatever you use. It tells the agent what to read, the five rules it may not break, and the seven steps that end with a signed webhook arriving at a receiver it wrote. Replace the key at the bottom with your own test key.
The prompt is English in both languages of this page, because the documentation, field names and error codes it sends the agent to read are English. A translation would send it looking for pages that do not exist.
You are integrating Nalpay — card payments, payment links and subscriptions for Saudi merchants — into this project.
## Read this first, in one fetch
https://paywithnal.com/llms.txt
That file is the whole API in plain text: authentication, test and live modes, every endpoint with the shape of its request and response, the one error envelope, idempotency, webhooks and how to verify their signatures, and the test cards. Read it before you write a line. The human quickstart is https://paywithnal.com/docs/quickstart and the OpenAPI document is linked from both.
## If you speak MCP, connect
url: https://mcp.paywithnal.com/mcp
auth: Authorization: Bearer <my key, below>
Tools: account_status, search_docs, create_payment_link, list_payments, create_subscription, simulate_event, refund_payment, kyc_status, kyc_submit_document. Prefer them over raw HTTP for anything exploratory. Every result states its mode on its first line — keep that line when you summarise.
## Rules you may not break
1. Work in test mode. The key below begins sk_test_ and can only ever touch test objects. Do not ask me for a live key.
2. No card number goes anywhere near this code. Nalpay has no field for one and refuses anything shaped like one. Cards are typed on Nalpay's hosted page.
3. Every POST sends an Idempotency-Key header, derived from the operation (order-1024-charge), never generated fresh inside a retry loop.
4. Amounts are whole numbers of minor units. 1500 is 15.00 SAR. There are no decimals anywhere in this API.
5. The key lives in an environment variable. Never in source, never in a log line, never in your reply to me.
## Do this, in order, and show me each result
1. GET /v1/account. Tell me the mode and capabilities.can_charge. Stop if can_charge is false.
2. Create a payment link for 15.00 SAR — POST /v1/payment_links with amount 1500 — and give me its url.
3. Write the smallest server-side client for this project's language: sends the bearer key, sends an Idempotency-Key on every POST, and turns the error envelope into an exception carrying type, code, param and the Nalpay-Request-Id response header.
4. Write a receiver at POST /hooks/nalpay that verifies Nalpay-Signature over the RAW body — HMAC-SHA256 of "{t}.{rawBody}" keyed with the whsec_ secret, hex, constant-time compare, reject if |now - t| > 300 seconds — de-duplicates on the Nalpay-Delivery header, answers 2xx immediately, and handles payment_paid and payment_refunded.
5. Register that receiver with POST /v1/webhook_endpoints. Store the secret it returns in the environment; it is shown once.
6. Prove it end to end without asking me to open anything: POST /v1/test/payment_links/{id}/pay on the link from step 2, with an Idempotency-Key and no body. That records the payment through the same ledger a real one goes through, settles the link and sends a signed payment_paid to your receiver. Show me the receiver's log line for the verified event, and the payment's simulated, fee and net. Then send {"outcome":"failed"} on a second link and show me the failure path too.
7. Add a README section: the environment variables, how to run the receiver, and exactly what changes when we go live.
My key: NALPAY_KEY=sk_test_[paste yours here]
Put it in .env, add .env to .gitignore, and never print it back to me.What a machine can read without a key
Five addresses, all public, all cached. An agent given nothing but our domain name can find out what Nalpay is and how to call it before it has been given anything.
- llms.txtThe whole API in one plain-text file: auth, modes, every endpoint with its request and response shape, errors, idempotency, webhooks, signature verification and the test cards. Generated from the specification, so it cannot fall behind it.https://paywithnal.com/llms.txt
- OpenAPIThe published specification for the /v1 surface. Written deliberately, not generated from our internals, and pinned by tests.https://paywithnal.com/openapi.json
- Agent skills indexThe four integration paths — a one-off charge, saving a card, a subscription, handling webhooks — each with its own short document naming the endpoints it uses.https://paywithnal.com/.well-known/agent-skills/index.json
- MCP serverThe same capabilities as tools. Authenticate with your API key as a bearer token; the key's prefix decides the mode and nothing else can.https://mcp.paywithnal.com/mcp
- QuickstartFor people. From an empty project to a paid test payment in about ten minutes, in curl, Node, C# and PHP.https://paywithnal.com/docs/quickstart
The tools
Nine, and each one calls the same code the public API does rather than a second implementation of it.
- account_status
- search_docs
- create_payment_link
- list_payments
- create_subscription
- simulate_event
- refund_payment
- kyc_status
- kyc_submit_document
Test mode is the default, and an agent holding a test key can only ever act in test. Every result states its mode on its first line, so a model summarising a long transcript cannot lose that detail. No tool ever returns an API key, a webhook signing secret or a card number. The two highlighted above are the guarded ones: simulate_event is refused outright with a live key, and refund_payment moves real money and is refused without an explicit confirmation.
Getting a key
Create one under Developers in your dashboard. A test key works immediately and can do everything a live key can, with no real money involved. Live keys are issued once your business verification is approved. Keys are shown once — we store only a hash — and can be revoked but never read back.