Testing Bulk Enrollment

Introduction

This guide drives the bulk-enrollment pipeline in the sandbox environment end to end: create a network bulk feed, trigger a synthetic ingestion, and watch cards and transactions appear, with no real network file delivery. Every request below runs against https://api.sandbox.astrada.co with a bearer token from Get a token.

In production, the network (Visa, Mastercard, Amex) delivers card files to Astrada on a schedule you do not control. In sandbox, you play the network: POST /simulate/ingestion/{network} writes a synthetic file into the same ingestion pipeline, drawing cards from fixed test card pools. Everything downstream of the file is the real pipeline, so the webhooks and resources you see here are what you will see in production.

1. Create a bulk feed

A feed registers the network reference that your files arrive under. Create one per network you want to test; this walkthrough uses Visa VCF. Use any of your subaccounts as {subaccountId} (or create one as in Testing Bank Linking). The networkReference format is network-specific, and the feed's network and type must match the simulate endpoint you call in step 2.

POST /network-bulk-feeds HTTP/1.1
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "networkReference": "1_123_45_6789",
  "network": "VISA",
  "type": "VCF",
  "subaccountId": "{subaccountId}"
}
curl -X POST https://api.sandbox.astrada.co/network-bulk-feeds \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "networkReference": "1_123_45_6789",
    "network": "VISA",
    "type": "VCF",
    "subaccountId": "{subaccountId}"
  }'
{
  "networkReference": "1_123_45_6789",
  "network": "VISA",
  "type": "VCF",
  "subaccountId": "{subaccountId}"
}

Response (201 Created): the feed resource with state: submitted.

{
  "id": "75ea3c99-6bf5-4b16-8f68-0cdc43d75806",
  "subaccountId": "...",
  "network": "VISA",
  "networkReference": "1_123_45_6789",
  "state": "submitted",
  "type": "VCF",
  "panFormat": "encrypted",
  "createdAt": "2026-08-28T09:00:00Z",
  "updatedAt": "2026-08-28T09:00:00Z"
}

Per-network bodies:

Networknetwork / typenetworkReference formatNotes
Visa VBDSVISA / VBDS (default)RPIC 1_123_45_6789Pull-based. cardCount (1-20, sandbox-only, VBDS-only) sizes an automatic starter fetch at creation, so cards appear without step 2.
Visa VCFVISA / VCFRPIC 1_123_45_6789File-based; nothing happens until step 2.
Mastercard SmartDataMASTERCARD / SMARTDATA (default)Delivery ID G1234567File-based.
Amex GLAMEX / GL (default)Recipient ID R123456File-based. panFormat: tokenized (sandbox-only, Amex-only) tests the tokenized-PAN variant; default is encrypted. On a tokenized feed newCardCount is unsupported and an omitted cardCount draws the whole tokenized pool (see the Sandbox API Reference).
📘

One feed per reference

POST /network-bulk-feeds returns 409 Conflict if a feed already exists for that networkReference. Reuse the existing feed and go straight to step 2; the simulate endpoints can be called on the same feed as often as you like.

2. Drive an ingestion

Each network has a simulate endpoint that writes a synthetic file for an existing feed. All four take the same three knobs and return 202 Accepted with an empty body; processing is asynchronous.

POST /simulate/ingestion/vcf HTTP/1.1
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "networkReference": "123_45_6789",
  "cardCount": 0,
  "newCardCount": 5,
  "transactionsPerCard": 3
}
curl -X POST https://api.sandbox.astrada.co/simulate/ingestion/vcf \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "networkReference": "123_45_6789",
    "cardCount": 0,
    "newCardCount": 5,
    "transactionsPerCard": 3
  }'
{
  "networkReference": "123_45_6789",
  "cardCount": 0,
  "newCardCount": 5,
  "transactionsPerCard": 3
}

The other three are the same call with a different path and reference format: POST /simulate/ingestion/smartdata ("networkReference": "G1234567"), POST /simulate/ingestion/amex-gl ("networkReference": "R123456"), and POST /simulate/ingestion/vbds ("networkReference": "1_12345_67890_54321"). Full body-param tables and response codes are in the Sandbox API Reference. One format quirk: VCF accepts the PIC without the region segment (123_45_6789) or the full RPIC used at feed creation; VBDS always takes the full RPIC.

How the three knobs behave on repeat calls:

  • cardCount replays the head of the network's test-card pool: the same cards every call, deduplicated once enrolled, like a re-delivered production file.
  • newCardCount draws cards this subaccount has never seen, so each call enrolls that many new cards and fires their cardsubscription.created webhooks.
  • transactionsPerCard generates that many transactions per included card, with fresh references and recent dates on every call, so repeats always produce transaction.created / transaction.updated webhooks, including for already-enrolled cards.
📘

Amex GL is the only network that differs

The knobs behave identically on Visa VCF, Mastercard SmartData, and Visa VBDS: an omitted cardCount defaults to 5, and newCardCount is always available. Amex GL is the exception on two points. An omitted cardCount draws 3 on an encrypted feed, or the whole enrollable tokenized pool on a panFormat: tokenized feed, rather than 5. And a tokenized feed has no new-card region, so any newCardCount > 0 on it is rejected with 400. See the Sandbox API Reference for the per-endpoint tables.

📘

Feed activation, and the VCF one-card safety net

A feed moves from submitted to active only once an ingestion creates at least one new card link. The first ingestion normally does this off the head cards cardCount draws by default. Visa VCF adds a safety net: if the subaccount has already enrolled those head cards, a head-only replay would deduplicate into nothing and leave the feed stuck, so on a feed that has never completed an ingestion newCardCount: 0 still draws one never-seen card (at most once per feed). This is VCF-only. SmartData, VBDS, and Amex GL activate off the head cards their default cardCount draws (5, or 3 / the tokenized pool for Amex), so they have no such forced draw.

3. See what was created

Processing usually completes within a minute. Observe the results through the normal read APIs and webhooks:

curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  "https://api.sandbox.astrada.co/network-bulk-feeds/{networkBulkFeedId}"

Response (200 OK): the feed, now state: active.

Then:

  • GET /cards?subaccountId={subaccountId} lists the enrolled cards, on the network's reserved test BIN.
  • GET /card-subscriptions?subaccountId={subaccountId} shows one subscription per card with enrollmentType: "network-bulk" and the networkBulkFeedId back-reference.
  • GET /cards/{cardId}/cardholder returns synthetic cardholder data (firstName, lastName, postalCode, corporate.employeeId; email only on Visa VCF).
  • With transactionsPerCard > 0, GET /transactions?subaccountId={subaccountId} fills up as clearings process.

Webhooks fired along the way, in the order you should see them: networkbulkfeed.statechanged (submitted to active), one cardsubscription.created per new card, then transactionmessage.created / transaction.created / transaction.updated per generated transaction. Payloads are in the Webhook Event Types reference.

The transactions above are the generic clearings generated during ingestion. Once you have enrolled cards, you can also drive a specific transaction shape against one of them with Simulate a transaction scenario — a catalog of predefined flows (purchases, refunds, partial clearings, FX conversions, hotel and car-rental pre-auths, the Amex auth lifecycles, and the multi-country Uber ride-share scenarios), useful for exercising authorization-to-expense matching against real network message sequences.

4. Test card pools

Simulated cards are drawn from fixed, reserved pools, matched by exact full PAN:

NetworkBINPAN length
Visa40450716
Mastercard51075516
Amex37000015

The full PAN lists and the head/new region semantics are on Test Card Pools. You rarely need the raw lists: cardCount and newCardCount draw from them for you.

Next steps


Did this page help you?