How Northstar offers Made Card
Northstar Home Loans is a made-up lender, built the way a Made partner would build it. It applied for partner access in Made's partner portal like any company, and it calls Made's staging partner API with its own keys.
It runs in its own network, with no private connection to Made. Every call to Made goes over the public internet, so what works here works for any partner.
Customer's browser --https--> madepartner.com (Northstar's server, its own AWS network)
| | client ID and secret, kept in AWS Secrets Manager
| +--https--> https://staging-api.getmcard.com/v1 (Made partner API)
+--Made Link window--> staging-app.madecard.com (Made's pages: apply, sign in, Made Card)
-
Get partner keys
Northstar applied at
test-partners.madecard.com, Made approved it, and Northstar keeps itsclient_idandclient_secretin AWS Secrets Manager. The server receives them as environment variables; the browser never sees them.In the partner portal Northstar lists its allowed origins:
https://madepartner.com,https://www.madepartner.com, andhttp://localhost:5601for local work. Made Link works only on pages from these origins. -
The server gets a partner JWT
POST https://staging-api.getmcard.com/v1/partners/auth { "client_id": "pk_...", "client_secret": "sk_..." } 200 { "data": { "access_token": "eyJ...", "expires_in": 3600 } }Northstar reuses the token until a minute before it expires, and gets a new one if Made says it expired.
-
The server starts a link session with what Northstar already knows
POST https://staging-api.getmcard.com/v1/partners/link/sessions Authorization: Bearer <partner JWT> { "kind": "apply", "prefill": { "first_name": "Jordan", "last_name": "Rivera", "email": "jordan@example.com", "phone": "7575550123", "address_line_1": "1 Main St", "city": "Virginia Beach", "state": "VA", "zipcode": "23451" }, "lock_fields": ["first_name", "last_name", "email", "address"] } 201 { "data": { "link_url": "https://staging-app.madecard.com/partner/apply?session=...", "expires_at": "..." } }Locked values can't be changed in Made's application. That is why Northstar's sign-up checks them against Made's rules first: letters and spaces in names, a street address rather than a P.O. Box, a 5-digit ZIP, and a 10-digit mobile number.
-
The page opens Made Link from the customer's click
<script src="https://staging-app.madecard.com/sdk/v1/made-link@1.0.0.js" integrity="sha384-fRjOnr9KxuJAAY+Ump/Gfx7Js/uSwQxb82AoHda8F5tR73rLzBqCFZTGET41xVMs" crossorigin="anonymous"></script> const handler = MadeLink.create({ linkUrl, onSuccess, onExit, onEvent }); applyButton.addEventListener("click", () => handler.open());Northstar loads a pinned release with its integrity hash, so the browser refuses a changed file. It creates the session when the page loads, so
open()runs inside the click and the browser doesn't block the Made window. The page sendsCross-Origin-Opener-Policy: same-origin-allow-popupsso the Made window can still report back. -
onSuccess: the server exchanges the public token
POST https://staging-api.getmcard.com/v1/partners/link/token { "public_token": "public_..." } 200 { "data": { "access_token": "link_...", "user_id": "5b0c9a1e-...", "expires_in": null } }Northstar encrypts the
link_token with AES-256-GCM, using a key from Secrets Manager, before it stores it with the customer in DynamoDB. It saves the link before answering the browser and never sends the token to the page. For an application,onSuccessfires as soon as the customer has a Made login, and the Made window stays open while they finish. -
Status, points, and transactions
GET https://staging-api.getmcard.com/v1/partners/customers/{user_id} GET https://staging-api.getmcard.com/v1/partners/customers/{user_id}/rewards GET https://staging-api.getmcard.com/v1/partners/customers/{user_id}/transactions?limit=5&offset=0account_idstays null until Made approves the application and the customer accepts the offer. Thenaccount_statusappears, and the points and transactions calls work (before that they answerPRTN_0014). Made has no webhooks, so Northstar checks when the customer visits and once a minute while the page is open. -
Open Made Card already signed in
POST https://staging-api.getmcard.com/v1/partners/sessions/launch { "access_token": "link_...", "target_path": "/dashboard/rewards" } 201 { "data": { "launch_url": "https://staging-app.madecard.com/partner/launch?code=...", "expires_in": 300 } } MadeLink.openLaunchUrl(launch_url);A launch URL works once, for 5 minutes. A customer who has no card yet lands on their application.
-
Reconnect
If Made answers
PRTN_0010, the customer's link was revoked. Northstar drops its stored token and offers "Connect it", akind: "login"session where the customer signs in to Made with any of their sign-in methods. The account page has a demo button that drops the token on purpose.
When Made Link ends without success
onExit code | Cause | What Northstar does |
|---|---|---|
POPUP_BLOCKED | The browser blocked the Made window | Asks the customer to allow pop-ups |
USER_CLOSED | The customer closed the Made window | Keeps the session so they can pick up again |
ORIGIN_NOT_ALLOWED | The page is not on an allowed origin | Explains it and starts a new session |
IDENTITY_MISMATCH | The Made profile doesn't match a locked field | Points to the details it shares |
SESSION_EXPIRED | The session expired or was already used | Starts a new session |
Try each path
- Open an account with sample details, then apply. Staging decisions are simulated, and no real credit check runs.
- Close the Made window before finishing to see
USER_CLOSED, or block pop-ups forPOPUP_BLOCKED. - Change your last name under "Details Northstar shares with Made", then connect again, to see
IDENTITY_MISMATCH. - Run Northstar locally on
http://127.0.0.1:5601, which is not an allowed origin, to seeORIGIN_NOT_ALLOWED.
Where it runs
An AWS network of its own (10.60.0.0/16) with no peering or other private path to Made. A load balancer with HTTPS sends traffic to one container on ECS Fargate. Customers, sessions, and the encrypted Made links live in DynamoDB, reached through a VPC endpoint. GitHub Actions deploys with a short-lived AWS role, so no AWS keys are stored anywhere.