For partners and their developers: how this demo lender integrates the Made Card partner API.
NNorthstar Home Loans

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)
  1. Get partner keys

    Northstar applied at test-partners.madecard.com, Made approved it, and Northstar keeps its client_id and client_secret in 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, and http://localhost:5601 for local work. Made Link works only on pages from these origins.

  2. 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.

  3. 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.

  4. 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 sends Cross-Origin-Opener-Policy: same-origin-allow-popups so the Made window can still report back.

  5. 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, onSuccess fires as soon as the customer has a Made login, and the Made window stays open while they finish.

  6. 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=0

    account_id stays null until Made approves the application and the customer accepts the offer. Then account_status appears, and the points and transactions calls work (before that they answer PRTN_0014). Made has no webhooks, so Northstar checks when the customer visits and once a minute while the page is open.

  7. 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.

  8. Reconnect

    If Made answers PRTN_0010, the customer's link was revoked. Northstar drops its stored token and offers "Connect it", a kind: "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 codeCauseWhat Northstar does
POPUP_BLOCKEDThe browser blocked the Made windowAsks the customer to allow pop-ups
USER_CLOSEDThe customer closed the Made windowKeeps the session so they can pick up again
ORIGIN_NOT_ALLOWEDThe page is not on an allowed originExplains it and starts a new session
IDENTITY_MISMATCHThe Made profile doesn't match a locked fieldPoints to the details it shares
SESSION_EXPIREDThe session expired or was already usedStarts a new session

Try each path

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.