# Card Issuing

Issue your own branded credit and pre-paid cards through Stark Infra.

Define your card product, create card holders, issue virtual or physical cards, control spending rules, authorize purchases in real time, manage embossing and shipping, support tokenization for digital wallets, settle invoices and reconcile every transaction.

This guide covers every resource you need to launch and operate a card program — from product configuration to spending and settlement.

NOTE: Read Core Concepts before continuing this guide.

**RESOURCE SUMMARY**

Issuing ProductConfigure the card product (credit/pre-paid, network, default rules).Issuing HolderRegister the individual or business that owns the cards.Issuing RuleSet spending controls (limits, intervals, merchant categories) on holders and cards.Issuing CardIssue virtual or physical cards and update their status.Issuing PurchaseAuthorize and reconcile every card purchase.Issuing DesignManage card design templates for physical embossing.Issuing Embossing KitReference embossing supplies (plastic, packaging).Issuing StockTrack raw card stock available for embossing.Issuing RestockReplenish card stock.Issuing Stock RuleSet low-balance alerts on your card stock.Issuing Embossing RequestOrder physical card embossing and shipping.Issuing Token / Token Request / Token DesignProvision cards into Apple Pay, Google Pay and other wallets.Issuing InvoiceGenerate Pix invoices to fund the issuing balance.Issuing WithdrawalMove funds from the issuing balance back to your workspace.Issuing Billing InvoiceSettle postpaid issuing usage through Pix billing invoices.Issuing Billing TransactionInspect the entries that compose a billing invoice.Issuing Preview InstallmentPreview upcoming installments before they are billed.Issuing Balance / TransactionConsult the issuing balance and ledger.

## Setup

For each environment (Sandbox and Production):

1. Create an account at Stark Bank.

2. Get in touch with your account manager to define your Issuing Product: pre-paid or credit card — the product determines network (Visa/Mastercard), default rules and BIN.

3. Inform the sub-issuer parameters to help@starkbank.com:

authorizationUrl: HTTPS URL to receive synchronous Issuing Purchase authorizations from your cards. If empty, the following parameters will dictate your authorizations.
defaultAuthorizationMaxAmount: Maximum amount in Reais (R$) that the default authorization system will approve in case your system does not respond correctly. Minimum is R$0,00.
defaultAuthorizationMinScore: Minimum score that the default authorization will approve. The score ranges from 0 (worst) to 1 (best).

4. Create a Webhook resource subscribing to issuing-card, issuing-holder, issuing-purchase, issuing-invoice, issuing-embossing-request events.

5. Fund the issuing balance with an Issuing Invoice before issuing your first card.

## Authorization flow

Every card purchase is authorized in real time. Stark Infra receives the authorization message from the card network and forwards it to the authorizationUrl you registered during Setup as a synchronous request.

We screen every purchase before calling you. Stark Infra first checks that the card and holder are active, that the purchase fits the spending controls set on the card and/or holder, that your issuing balance as a sub issuer covers it, and that the merchant, country, payment method and installments are eligible — and it computes a fraud score for the purchase. If any of these checks fails we deny the purchase ourselves and your authorizationUrl is never called; only purchases that clear them are forwarded to you (with the score) for the final decision.

Your endpoint must answer within 2 seconds, always with HTTP status 200 — even when you decline. To approve, return {"authorization": {"status": "approved"}} (you may also override the amount - by a smaller one, if the field isPartialAllowed is true - or attach tags). To decline, return {"authorization": {"status": "denied", "reason": "..."}} with one of the accepted denial reasons — download the full list here.

If you do not respond in time (or you never registered an authorizationUrl), the request is not automatically denied: the default authorization protocol takes over and decides on your behalf. It approves the purchase when the amount is within the defaultAuthorizationMaxAmount and the purchase score is at least the defaultAuthorizationMinScore you configured during Setup, and declines it otherwise (reason standInDenial). A timeout can therefore still result in an approval.

NOTE: Because the default authorization protocol can approve or deny a purchase your system was still processing, prepare for concurrency — you may receive the approved / denied Issuing Purchase events for a purchase before your own authorization logic finishes. Reconcile by endToEndId so the same purchase is not registered twice.

## Issuing Card Overview

An Issuing Card is a credit or pre-paid card you issue to an Issuing Holder.

Virtual vs. physical. A virtual card is created instantly with status "active" and can be used for online purchases right away. A physical card is created with status "pending": it must be embossed and shipped through an Issuing Embossing Request, and then activated (set its status to "active" together with a PIN) before it can be used.

Getting a productId. Every card must reference a productId — the card product, which sets its BIN range. A product defines the card's network (Visa/Mastercard), funding type (credit or pre-paid), holder type (business or individual) and tier (e.g.: Black, Platinum). See the Issuing Product section to learn more.

Spending controls. Pass a "rules" list when creating or updating a card (or its holder) to cap how much can be spent per interval and to restrict categories, countries or methods. Each rule tracks how much was already spent in the current interval (counterAmount). See the Issuing Rule section for the full rule object and how to set or change it.

### Card Statuses

Each Issuing Card has a status that reflects where it is in its life cycle:

| Status | Description |
| --- | --- |
| pending | A physical card was created but is not usable yet. Activate it by setting its status to "active" together with a PIN. Virtual cards skip this status. |
| active | The card is active and can be used for purchases. |
| blocked | The card is temporarily blocked. You can unblock it by setting its status back to "active". |
| expired | The card has passed its expiration date and can no longer be used. |
| canceled | The card was permanently canceled. This is irreversible. |

### Card Logs

Every time you or Stark Infra change an Issuing Card, we create a Log entry. Logs let you audit a card's full history — each status change, rule update and so on.

Query the card logs with GET /v2/issuing-card/log (filter by cardIds, types, after / before), or fetch a single one with GET /v2/issuing-card/log/:id. Check the API reference for the full list of log fields and types.

### Creating a Card

`POST /v2/issuing-card`

Create one or more cards by sending a "cards" list. Each card needs holderName, holderTaxId, holderExternalId and a productId.

Holder resolution. The card's holder is identified by its holderExternalId. If no holder with that external id exists under your sub-issuer yet, one is created from the holderName and holderTaxId you send; if a holder with that external id already exists, the card is attached to it. In that case the holderName and holderTaxId must match the existing holder — otherwise the request is rejected.

Set "type" to "virtual" (the default — instantly active) or "physical" (created as "pending", needs embossing, shipment and activation).

Billing address. If you omit the address, the card inherits your sub-issuer's registered address by default. The address is all-or-nothing: send the complete address (streetLine1, streetLine2, district, city, stateCode, zipCode) or none — a partial address is rejected.

**Request**

```python
import starkinfra

cards = starkinfra.issuingcard.create([
    starkinfra.IssuingCard(
        holder_name="Tony Stark",
        holder_tax_id="012.345.678-90",
        holder_external_id="my-holder-id-1234",
        product_id="654321",
        type="physical",
        display_name="TONY STARK",
        tags=["department: tech", "team: backend"]
    )
])

for card in cards:
    print(card)
```

**Response**

```python
IssuingCard(
    id=5715709195239424,
    holder_id=5155165527080960,
    holder_name=Tony Stark,
    holder_tax_id=012.345.678-90,
    holder_external_id=my-holder-id-1234,
    display_name=TONY STARK,
    product_id=654321,
    type=physical,
    status=pending,
    rules=[],
    street_line_1=Av. Faria Lima, 1844,
    street_line_2=10 andar,
    district=Itaim Bibi,
    city=Sao Paulo,
    state_code=SP,
    zip_code=01311-000,
    tags=['department: tech', 'team: backend'],
    number=**** **** **** 1234,
    security_code=***,
    expiration=****-**-**T**:**:**.******+00:00,
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Unblocking (activating) a physical card

`PATCH /v2/issuing-card/:id`

A physical card is created as "pending". To activate it, set its status to "active" together with a "pin" (a numeric string, 4 to 6 digits) — its first activation always requires a PIN.

NOTE: To unblock a card that was merely blocked (and had already been activated before), you can send "status": "active" on its own, without a PIN.

**Request**

```python
import starkinfra

card = starkinfra.issuingcard.update(
    id="5715709195239424",
    status="active",
    pin="1234"
)

print(card)
```

**Response**

```python
IssuingCard(
    id=5715709195239424,
    holder_id=5155165527080960,
    holder_name=Tony Stark,
    holder_tax_id=012.345.678-90,
    holder_external_id=my-holder-id-1234,
    display_name=TONY STARK,
    product_id=654321,
    type=physical,
    status=active,
    rules=[],
    street_line_1=Av. Faria Lima, 1844,
    street_line_2=10 andar,
    district=Itaim Bibi,
    city=Sao Paulo,
    state_code=SP,
    zip_code=01311-000,
    tags=['department: tech', 'team: backend'],
    number=**** **** **** 1234,
    security_code=***,
    expiration=****-**-**T**:**:**.******+00:00,
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Blocking a card

`PATCH /v2/issuing-card/:id`

Temporarily block a card by setting its status to "blocked". A blocked card declines purchases until you unblock it.

**Request**

```python
import starkinfra

card = starkinfra.issuingcard.update(
    id="5715709195239424",
    status="blocked"
)

print(card)
```

**Response**

```python
IssuingCard(
    id=5715709195239424,
    holder_id=5155165527080960,
    holder_name=Tony Stark,
    holder_tax_id=012.345.678-90,
    holder_external_id=my-holder-id-1234,
    display_name=TONY STARK,
    product_id=654321,
    type=physical,
    status=blocked,
    rules=[],
    street_line_1=Av. Faria Lima, 1844,
    street_line_2=10 andar,
    district=Itaim Bibi,
    city=Sao Paulo,
    state_code=SP,
    zip_code=01311-000,
    tags=['department: tech', 'team: backend'],
    number=**** **** **** 1234,
    security_code=***,
    expiration=****-**-**T**:**:**.******+00:00,
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Updating the Card PIN

`PATCH /v2/issuing-card/:id`

Set or change a card's PIN by sending "pin" (a numeric string, 4 to 6 digits) on update.

The PIN is write-only and is never returned — inform "expand=isPinDefined" when fetching the card to check whether a PIN is set.

**Request**

```python
import starkinfra

card = starkinfra.issuingcard.update(
    id="5715709195239424",
    pin="5678"
)

print(card)
```

**Response**

```python
IssuingCard(
    id=5715709195239424,
    holder_id=5155165527080960,
    holder_name=Tony Stark,
    holder_tax_id=012.345.678-90,
    holder_external_id=my-holder-id-1234,
    display_name=TONY STARK,
    product_id=654321,
    type=physical,
    status=active,
    rules=[],
    street_line_1=Av. Faria Lima, 1844,
    street_line_2=10 andar,
    district=Itaim Bibi,
    city=Sao Paulo,
    state_code=SP,
    zip_code=01311-000,
    tags=['department: tech', 'team: backend'],
    number=**** **** **** 1234,
    security_code=***,
    expiration=****-**-**T**:**:**.******+00:00,
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Canceling a card

`DELETE /v2/issuing-card/:id`

Permanently cancel a card by its id. The card's status becomes "canceled". This action is irreversible.

**Request**

```python
import starkinfra

card = starkinfra.issuingcard.cancel("5715709195239424")

print(card)
```

**Response**

```python
IssuingCard(
    id=5715709195239424,
    holder_id=5155165527080960,
    holder_name=Tony Stark,
    holder_tax_id=012.345.678-90,
    holder_external_id=my-holder-id-1234,
    display_name=TONY STARK,
    product_id=654321,
    type=physical,
    status=canceled,
    rules=[],
    street_line_1=Av. Faria Lima, 1844,
    street_line_2=10 andar,
    district=Itaim Bibi,
    city=Sao Paulo,
    state_code=SP,
    zip_code=01311-000,
    tags=['department: tech', 'team: backend'],
    number=**** **** **** 1234,
    security_code=***,
    expiration=****-**-**T**:**:**.******+00:00,
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Getting a Card's Logs

`GET /v2/issuing-card/log`

Audit a card's history by querying its logs with the "cardIds" filter. Each status change creates a Log — a card that was created, activated, blocked and then canceled returns one log per step. The "card" field in each log is a full representation of the Issuing Card as it was at that point in its history (shown abbreviated with "...").

You can also filter by "types" (created, unblocked, blocked, expired, canceled, updated) and "after" / "before", or fetch a single log with GET /v2/issuing-card/log/:id.

**Request**

```python
import starkinfra

logs = starkinfra.issuingcard.log.query(
    card_ids=["5715709195239424"]
)

for log in logs:
    print(log)
```

**Response**

```python
Log(
    id=6724771005489152,
    type=created,
    card=IssuingCard(id=5715709195239424, status=pending, ...),
    created=2022-01-01 00:00:00
)
Log(
    id=5547499534213120,
    type=unblocked,
    card=IssuingCard(id=5715709195239424, status=active, ...),
    created=2022-02-01 00:00:00
)
Log(
    id=5189683645874176,
    type=blocked,
    card=IssuingCard(id=5715709195239424, status=blocked, ...),
    created=2022-03-01 00:00:00
)
Log(
    id=6284441486065664,
    type=canceled,
    card=IssuingCard(id=5715709195239424, status=canceled, ...),
    created=2022-06-01 00:00:00
)
```

## Issuing Holder Overview

An Issuing Holder is the person or business that owns the cards you issue. Each Issuing Card is attached to a holder, and one holder can own many cards.

Individual vs. business. A holder's type is derived from its taxId: a CPF (11 digits) makes it an individual, a CNPJ (14 digits) a business. This must match the holderType of the card's Issuing Product — issuing a business-product card to a CPF holder (or vice-versa) is rejected.

externalId. Each holder carries an externalId — your own reference for it, unique among your active and blocked holders. It is what lets you create cards by holderExternalId without first looking up the holder's Stark id, and it stops you from accidentally creating the same holder twice.

Spending controls. A holder can carry its own "rules" list, applied on top of each card's rules — handy to cap total spend across every card a holder owns. Pass a "rules" list on create or update. The "rules" field is always returned on the holder; pass "expand=rules" to also get each rule's live spending counter (counterAmount). See the Issuing Rule section for the full rule object and how to set it.

### Holder Statuses

Each Issuing Holder has a status that reflects where it is in its life cycle:

| Status | Description |
| --- | --- |
| active | The holder is active and its cards can be used. New holders are created in this status. |
| blocked | The holder is temporarily blocked. You can unblock it by setting its status back to "active". |
| canceled | The holder was permanently canceled, together with all of its cards. This is irreversible. |

### Holder Logs

Every time you or Stark Infra change an Issuing Holder, we create a Log entry. Logs let you audit a holder's full history — each status change and rule update.

Query the holder logs with GET /v2/issuing-holder/log (filter by holderIds, types, after / before), or fetch a single one with GET /v2/issuing-holder/log/:id. The log types are: created, unblocked, blocked, updated, canceled.

### Creating a Holder

`POST /v2/issuing-holder`

Create one or more holders by sending a "holders" list (up to 100 at a time). Each holder needs a name, a taxId and an externalId. New holders start with status "active".

The externalId must be unique among your active and blocked holders: reusing the externalId of an existing one is rejected.

You can attach a "rules" list at creation to set holder-level spending limits. The created rules are returned either way; pass "expand=rules" in the query string to also get each rule's counterAmount.

**Request**

```python
import starkinfra

holders = starkinfra.issuingholder.create(
    [
        starkinfra.IssuingHolder(
            name="Tony Stark",
            tax_id="012.345.678-90",
            external_id="my-holder-id-1234",
            tags=["department: tech", "team: backend"],
            rules=[
                starkinfra.IssuingRule(
                    name="General BRL",
                    interval="day",
                    amount=100000,
                    currency_code="BRL"
                )
            ]
        )
    ],
    expand=["rules"]
)

for holder in holders:
    print(holder)
```

**Response**

```python
IssuingHolder(
    id=5715709195239424,
    name=Tony Stark,
    tax_id=012.345.678-90,
    external_id=my-holder-id-1234,
    status=active,
    tags=['department: tech', 'team: backend'],
    rules=[
        IssuingRule(
            id=5155165527080960,
            name=General BRL,
            amount=100000,
            interval=day,
            currency_code=BRL,
            currency_name=Brazilian Real,
            currency_symbol=R$,
            counter_amount=0,
            schedule=,
            purposes=[],
            categories=[],
            countries=[],
            methods=[],
            merchants=[]
        )
    ],
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Blocking a Holder

`PATCH /v2/issuing-holder/:id`

Temporarily block a holder by setting its status to "blocked". While blocked, the holder's cards decline purchases until you unblock it.

**Request**

```python
import starkinfra

holder = starkinfra.issuingholder.update(
    id="5715709195239424",
    status="blocked"
)

print(holder)
```

**Response**

```python
IssuingHolder(
    id=5715709195239424,
    name=Tony Stark,
    tax_id=012.345.678-90,
    external_id=my-holder-id-1234,
    status=blocked,
    tags=['department: tech', 'team: backend'],
    rules=[
        IssuingRule(
            id=5155165527080960,
            name=General BRL,
            amount=100000,
            interval=day,
            currency_code=BRL,
            schedule=,
            purposes=[],
            categories=[],
            countries=[],
            methods=[],
            merchants=[]
        )
    ],
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Unblocking a Holder

`PATCH /v2/issuing-holder/:id`

Reactivate a blocked holder by setting its status back to "active". Only "active" and "blocked" are accepted on update — to permanently retire a holder use the cancel route below.

**Request**

```python
import starkinfra

holder = starkinfra.issuingholder.update(
    id="5715709195239424",
    status="active"
)

print(holder)
```

**Response**

```python
IssuingHolder(
    id=5715709195239424,
    name=Tony Stark,
    tax_id=012.345.678-90,
    external_id=my-holder-id-1234,
    status=active,
    tags=['department: tech', 'team: backend'],
    rules=[
        IssuingRule(
            id=5155165527080960,
            name=General BRL,
            amount=100000,
            interval=day,
            currency_code=BRL,
            schedule=,
            purposes=[],
            categories=[],
            countries=[],
            methods=[],
            merchants=[]
        )
    ],
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Updating a Holder's Rules

`PATCH /v2/issuing-holder/:id`

Send a "rules" list on update to replace the holder's spending rules — the new list overwrites the old one. You can also update "name" and "tags" in the same call.

Rules are echoed back abbreviated unless you pass "expand=rules". See the Issuing Rule section for the full rule object.

**Request**

```python
import starkinfra

holder = starkinfra.issuingholder.update(
    id="5715709195239424",
    rules=[
        starkinfra.IssuingRule(
            name="General BRL",
            interval="month",
            amount=500000,
            currency_code="BRL"
        )
    ]
)

print(holder)
```

**Response**

```python
IssuingHolder(
    id=5715709195239424,
    name=Tony Stark,
    tax_id=012.345.678-90,
    external_id=my-holder-id-1234,
    status=active,
    tags=['department: tech', 'team: backend'],
    rules=[
        IssuingRule(
            id=5155165527080960,
            name=General BRL,
            amount=500000,
            interval=month,
            currency_code=BRL,
            schedule=,
            purposes=[],
            categories=[],
            countries=[],
            methods=[],
            merchants=[]
        )
    ],
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Canceling a Holder

`DELETE /v2/issuing-holder/:id`

Permanently cancel a holder by its id. The holder's status becomes "canceled" and all of its cards are canceled along with it. This action is irreversible.

**Request**

```python
import starkinfra

holder = starkinfra.issuingholder.cancel("5715709195239424")

print(holder)
```

**Response**

```python
IssuingHolder(
    id=5715709195239424,
    name=Tony Stark,
    tax_id=012.345.678-90,
    external_id=my-holder-id-1234,
    status=canceled,
    tags=['department: tech', 'team: backend'],
    rules=[
        IssuingRule(
            id=5155165527080960,
            name=General BRL,
            amount=100000,
            interval=day,
            currency_code=BRL,
            schedule=,
            purposes=[],
            categories=[],
            countries=[],
            methods=[],
            merchants=[]
        )
    ],
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Getting a Holder's Logs

`GET /v2/issuing-holder/log`

Audit a holder's history by querying its logs with the "holderIds" filter. Each change creates a Log — a holder that was created, blocked and then unblocked returns one log per step. The "holder" field in each log is a representation of the Issuing Holder as it was at that point in its history.

You can also filter by "types" (created, unblocked, blocked, updated, canceled) and "after" / "before", or fetch a single log with GET /v2/issuing-holder/log/:id.

**Request**

```python
import starkinfra

logs = starkinfra.issuingholder.log.query(
    holder_ids=["5715709195239424"]
)

for log in logs:
    print(log)
```

**Response**

```python
Log(
    id=6724771005489152,
    type=created,
    holder=IssuingHolder(id=5715709195239424, status=active, ...),
    created=2022-01-01 00:00:00
)
Log(
    id=5189683645874176,
    type=blocked,
    holder=IssuingHolder(id=5715709195239424, status=blocked, ...),
    created=2022-03-01 00:00:00
)
Log(
    id=5547499534213120,
    type=unblocked,
    holder=IssuingHolder(id=5715709195239424, status=active, ...),
    created=2022-04-01 00:00:00
)
```

## Issuing Rule Overview

The Issuing Rule object represents the spending controls you attach to an Issuing Holder or Issuing Card.

A rule caps how much can be spent within a time interval and optionally restricts spending to specific merchant categories, countries or card methods.

Rules are nested objects — you never create them on their own. You set them when creating or updating a holder or card.

The spending counter. Each rule keeps a "counterAmount": how much has already been spent against it in the current interval. We update it after every purchase that moves the limit — it goes up when a purchase is approved, and back down when a purchase is reversed or confirmed for a smaller amount than was authorized. It resets to 0 when the rule's interval rolls over.

Reading the remaining balance. The amount still available on a rule is simply "amount" − "counterAmount". For example, a R$ 1.500,00 rule (amount = 150000) that already has counterAmount = 40000 still has R$ 1.100,00 left to spend in the current interval.

The rule object is always returned on its holder or card — you do not need any expand to read a rule's name, interval, amount and restrictions. Passing "expand=rules" simply enriches each returned rule with its live "counterAmount" (plus the currency name/symbol and the fully-resolved category, country and method objects).

### The Issuing Rule object

**Parameters**

| Name | Type | Description |
| --- | --- | --- |
| `name` | STRING | Rule name, used to identify it. Example: "Travel". |
| `interval` | STRING | Interval after which the rule counter resets. Options: "instant", "day", "week", "month", "year", "lifetime". NOTE: "lifetime" never resets on its own — a rule with this interval keeps accumulating its counterAmount indefinitely. The only way to clear it is to replace the rule yourself: update the holder or card with that rule's id omitted, which starts a fresh rule (and a fresh counter) in its place. |
| `amount` | INTEGER | Maximum amount that can be spent within the interval, in cents. Example: 150000 (R$ 1.500,00). |
| `currencyCode` | STRING | Currency the rule's amount is measured in (e.g. "BRL", "USD"). The rule does not limit spending to this currency — purchases in other currencies are converted and counted against the limit. Example: a R$ 100,00 limit covers a R$ 100,00 purchase, or a US$ 20,00 purchase when 1 USD = 5 BRL. |
| `schedule` | STRING | Optional schedule dictating when the rule can be used. Some examples: "everyday from 09:00 to 18:00 in America/Sao_Paulo" — every day, 09:00–18:00 São Paulo time; "every monday, wednesday, friday from 08:00 to 12:00 in America/Sao_Paulo" — only those weekdays, mornings; "every saturday, sunday" — weekends, all day, in UTC; |
| `purposes` | LIST OF STRINGS | Optional list of transaction purposes the rule applies to. Options: "purchase", "withdrawal", "verification". The rule then limits only purchases of those purposes; omit it to allow any purposes. Example: ["purchase", "verification"] if you want us to automatically deny withdrawal. |
| `categories` | LIST OF OBJECTS | Optional list of MerchantCategory objects the rule applies to. Restricts spending to the given merchant categories. |
| `countries` | LIST OF OBJECTS | Optional list of MerchantCountry objects the rule applies to. Restricts spending to the given countries. |
| `methods` | LIST OF OBJECTS | Optional list of CardMethod objects the rule applies to. Restricts spending to the given payment methods. |
| `id` | STRING | Unique id of the rule. Inform it when updating to preserve the rule's counters; omit it to create a fresh rule in its place. |
| `counterAmount` | INTEGER | Amount already spent against this rule in the current interval, in cents. It increases when a purchase is approved and decreases when one is reversed or confirmed for a smaller amount, resetting to 0 when the interval rolls over. The rule's remaining balance is amount − counterAmount. Only present when you query the holder or card with "expand=rules". |
| `currencyName` | STRING | Currency full name. Returned only. Example: "Brazilian Real". |
| `currencySymbol` | STRING | Currency symbol. Returned only. Example: "R$". |

### Creating with Spending Controls

`POST /v2/issuing-holder`

Set the initial rules when you create a holder (or card) by passing a "rules" list of IssuingRule objects.

Omit each rule's id on creation — Stark Infra assigns one, which you later reuse to preserve the rule's counters when updating.

**Request**

```python
import starkinfra

holders = starkinfra.issuingholder.create([
    starkinfra.IssuingHolder(
        name="Tony Stark",
        tax_id="012.345.678-90",
        external_id="my-holder-id-1234",
        tags=["department: tech"],
        rules=[
            starkinfra.IssuingRule(
                name="Travel",
                amount=150000,
                currency_code="BRL",
                interval="month",
                schedule="everyday from 09:00 to 18:00 in America/Sao_Paulo",
                purposes=["purchase", "withdrawal"],
                categories=[
                    starkinfra.MerchantCategory(type="transportation")
                ]
            ),
            starkinfra.IssuingRule(
                name="General",
                amount=100000,
                currency_code="BRL",
                interval="week",
                schedule="everyday from 09:00 to 18:00 in America/Sao_Paulo",
                purposes=["purchase", "withdrawal"]
            )
        ]
    )
])

for holder in holders:
    print(holder)
```

**Response**

```python
IssuingHolder(
    id=5828610602565632,
    name=Tony Stark,
    tax_id=012.345.678-90,
    external_id=my-holder-id-1234,
    status=active,
    tags=['department: tech'],
    rules=[
        IssuingRule(
            id=5265660649144320,
            name=Travel,
            amount=150000,
            interval=month,
            currency_code=BRL,
            currency_name=Brazilian Real,
            currency_symbol=R$,
            counter_amount=0,
            schedule=everyday from 09:00 to 18:00 in America/Sao_Paulo,
            purposes=['purchase', 'withdrawal'],
            categories=[MerchantCategory(code=, name=, number=, type=transportation)],
            countries=[],
            methods=[]
        ),
        IssuingRule(
            id=6743406558576640,
            name=General,
            amount=100000,
            interval=week,
            currency_code=BRL,
            currency_name=Brazilian Real,
            currency_symbol=R$,
            counter_amount=0,
            schedule=everyday from 09:00 to 18:00 in America/Sao_Paulo,
            purposes=['purchase', 'withdrawal'],
            categories=[],
            countries=[],
            methods=[]
        )
    ],
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Updating Spending Controls

`PATCH /v2/issuing-holder/:id`

You may need to add or change the rules on your holders and cards.

NOTE: When you update the rules list, inform each existing rule with its id to preserve its counters. To reset a rule, omit the id and a new rule is created in place of the old one.

Send the full list you want the resource to end with — any rule absent from the list is removed. The same pattern applies to Issuing Card updates.

**Request**

```python
import starkinfra

holder = starkinfra.issuingholder.update(
    id="5828610602565632",
    rules=[
        starkinfra.IssuingRule(
            id="5265660649144320",
            name="Travel",
            amount=150000,
            currency_code="BRL",
            interval="month",
            schedule="everyday from 09:00 to 18:00 in America/Sao_Paulo",
            purposes=["purchase", "withdrawal"],
            categories=[
                starkinfra.MerchantCategory(type="transportation")
            ]
        ),
        starkinfra.IssuingRule(
            name="General",
            amount=100000,
            currency_code="BRL",
            interval="week",
            schedule="everyday from 09:00 to 18:00 in America/Sao_Paulo",
            purposes=["purchase", "withdrawal"]
        )
    ]
)

holder = starkinfra.issuingholder.get(holder.id, expand=["rules"])

print(holder)
```

**Response**

```python
IssuingHolder(
    id=5828610602565632,
    name=Tony Stark,
    tax_id=012.345.678-90,
    external_id=my-holder-id-1234,
    status=active,
    tags=['department: tech'],
    rules=[
        IssuingRule(
            id=5265660649144320,
            name=Travel,
            amount=150000,
            interval=month,
            currency_code=BRL,
            currency_name=Brazilian Real,
            currency_symbol=R$,
            counter_amount=0,
            schedule=everyday from 09:00 to 18:00 in America/Sao_Paulo,
            purposes=['purchase', 'withdrawal'],
            categories=[MerchantCategory(code=, name=, number=, type=transportation)],
            countries=[],
            methods=[]
        ),
        IssuingRule(
            id=6743406558576640,
            name=General,
            amount=100000,
            interval=week,
            currency_code=BRL,
            currency_name=Brazilian Real,
            currency_symbol=R$,
            counter_amount=0,
            schedule=everyday from 09:00 to 18:00 in America/Sao_Paulo,
            purposes=['purchase', 'withdrawal'],
            categories=[],
            countries=[],
            methods=[]
        )
    ],
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

## Issuing Purchase Overview

An Issuing Purchase is a transaction made with an Issuing Card. You never create purchases yourself — they appear in real time, when a cardholder pays at a merchant and the authorization is approved.

How purchases reach you. When a card is used, the network sends us an authorization request. We fire an issuing-purchase Webhook event so you can approve or deny it on the spot; the approved (or denied) purchase is then persisted and can be queried here. See the Handling Purchase Events section to learn how to answer authorizations and react to status changes.

Merchant vs. issuer amount. When a purchase is made in a currency other than your own, the same money is recorded twice: merchantAmount (in merchantCurrencyCode, what the merchant charged) and issuerAmount (in issuerCurrencyCode, what is debited from your Issuing Balance after FX conversion). All amounts are integers in cents.

Installments. installmentCount is how many installments the purchase was split into (1 for a single, upfront charge). Each installment settles separately, so a single purchase can produce more than one confirmation over time.

endToEndId. Every purchase carries an endToEndId, the unique transaction identifier shared across the network. Use it to reconcile a purchase with the acquirer or to filter the listing (endToEndIds).

Ledger link. Once a purchase affects your balance, issuingTransactionIds lists the Issuing Transaction entries it generated — that's the bridge between a purchase and your ledger.

metadata. When a purchase came from an authorization you answered, metadata carries its authorizationId; otherwise it's an empty object.

### Purchase Statuses

Each Issuing Purchase has a status that reflects where it is in its life cycle. Status changes are driven by the network and the sub-issuer — not by you:

| Status | Description |
| --- | --- |
| approved | The authorization request was approved by the sub-issuer or by the network/issuer default protocol. The amount is reserved for future settlement. |
| confirmed | The purchase was settled by the network. At this point the money is transferred through the network. |
| voided | The purchase was fully reversed and is no longer in effect. |
| canceled | An approved purchase was canceled by the acquirer. The reserved amount goes back to your Issuing Balance. |
| denied | The authorization request was denied by the sub-issuer or by the network/issuer default protocol. |

### Purchase Logs

Every change to an Issuing Purchase creates a Log entry. Logs are how you follow a purchase through its life cycle: each authorization, settlement, reversal and edit becomes one log. Whenever a new Log is created, we fire a Webhook to your registered URL (see Handling Purchase Events).

A log's type is one of: approved, denied, confirmed, reversed (some amount was reversed, partially or fully), voided (the purchase was fully reversed), canceled and updated (you set a description or tags). Note that reversed is a log type but not a purchase status — a partial reversal logs reversed while the purchase stays confirmed.

Each log carries the purchase as it was at that point (shown abbreviated with "..."), plus installment (which installment the log refers to) and issuingTransactionId (the ledger entry tied to this step). Query logs with GET /v2/issuing-purchase/log or fetch one with GET /v2/issuing-purchase/log/:id.

### Corner Cases

A few unusual authorization scenarios are worth knowing about:

Corner case 1. The network does not send us the authorization request and instead only notifies us that it approved or denied a purchase, without our (or your) permission — for example, in the unlikely event of a communication failure between us and the network.

Corner case 2. Common at gas stations in the USA: the cardholder sends the authorization request before filling the tank just to check the card data. Once the fuel is in the car and can no longer be retrieved, we are only notified of the final value charged, since it can no longer be denied.

Corner case 3. The merchant sends cash to the holder, producing an Issuing Purchase with a negative amount (e.g. a bonus or benefit to a recurring customer). In this case the interchange is retained by the acquirer and does not reach us (or you).

Corner case 4. New authorization requests can reference the same purchase, incrementing the original amount. These incremental authorizations share the same endToEndId as the previous ones — for example, when a ride-hailing app extends a previous authorization after a route change or delay.

### Default Authorization Cases

These cases happen when your sub-issuer fails to respond to the authorization request in time and our default authorization protocol is triggered (governed by the defaultAuthorizationMaxAmount and defaultAuthorizationMinScore you set during setup).

Case 1. The authorization request reaches your endpoint, but the default authorization protocol denies an authorization your system had approved, or vice-versa:

Case 2. When the default authorization protocol is triggered, the request may be approved with a different amount than expected. For example, when the merchant allows a partial amount and the holder doesn't have the full amount, your system could approve a smaller amount — but if the default protocol is triggered, the purchase may be fully approved instead.

NOTE: prepare for concurrency — we may have already detected a timeout on your endpoint and sent you the corresponding approved/denied event while your system is still processing the authorization. If left unchecked, you could register two purchases with the same endToEndId.

### Parsing an authorization request

When a holder uses our card, Stark Infra receives an authorization message from the card network, and POSTs a synchronous authorization request to the authorizationUrl you registered during Setup — see the Authorization flow for the full contract.

Verify the request really came from us before trusting it: pass the raw body and the Digital-Signature header to the SDK's parse method, which checks the signature against Stark Infra's public key and returns the Issuing Purchase to analyze. Then answer with {"authorization": {"status": "approved"}} (optionally overriding amount or attaching tags) or {"authorization": {"status": "denied", "reason": "..."}} with one of the accepted denial reasons.

**Request**

```python
import starkinfra

request = listen()

authorization = starkinfra.issuingpurchase.parse(
    content=request.data.decode("utf-8"),
    signature=request.headers["Digital-Signature"],
)

print(authorization)

denyReason = yourAuthorizationLogic(authorization)

if denyReason:
    return sendResponse(starkinfra.issuingpurchase.response(status="denied", reason=denyReason))
return sendResponse(starkinfra.issuingpurchase.response(status="approved"))
```

**Response**

```python
IssuingPurchase(
    acquirer_id=5656565656565656,
    amount=5000,
    card_id=7834882116598271,
    card_tags=[],
    end_to_end_id=79fcf95d-1dfb-40db-9fa8-679e329c58f0,
    holder_id=5917814565109760,
    holder_tags=[],
    is_partial_allowed=False,
    issuer_amount=5000,
    issuer_currency_code=BRL,
    merchant_amount=5000,
    merchant_category_code=fastFoodRestaurants,
    merchant_country_code=BRA,
    merchant_currency_code=BRL,
    merchant_fee=0,
    merchant_id=5656565656565656,
    merchant_name=Stark Store,
    method_code=chip,
    purpose=purchase,
    score=0.9775689825217906,
    tax=0,
    wallet_id=
)
```

### Listing purchases

`GET /v2/issuing-purchase`

List purchases in pages of up to 100. Filter by "status" (one of the purchase statuses), "cardIds", "holderIds", "endToEndIds", "ids", "tags" and the "after" / "before" date range.

The "issuerAmount" / "merchantAmount" split and the populated "metadata.authorizationId" are visible here — use them to reconcile each purchase against the merchant charge and the authorization you answered.

**Request**

```python
import starkinfra

purchases = starkinfra.issuingpurchase.query(
    limit=10,
    status="approved"
)

for purchase in purchases:
    print(purchase)
```

**Response**

```python
IssuingPurchase(
    id=5715709195239424,
    holder_name=Tony Stark,
    product_id=654321,
    card_id=7834882116598271,
    card_ending=1234,
    purpose=purchase,
    installment_count=1,
    amount=5000,
    tax=0,
    issuer_amount=5000,
    issuer_currency_code=BRL,
    issuer_currency_symbol=R$,
    merchant_amount=5000,
    merchant_currency_code=BRL,
    merchant_currency_symbol=R$,
    merchant_category_code=fastFoodRestaurants,
    merchant_category_number=5814,
    merchant_category_type=food,
    merchant_country_code=BRA,
    acquirer_id=5656565656565656,
    merchant_id=5656565656565656,
    merchant_name=Stark Store,
    merchant_fee=0,
    wallet_id=None,
    method_code=chip,
    score=0.9775689825217906,
    end_to_end_id=79fcf95d-1dfb-40db-9fa8-679e329c58f0,
    tags=[],
    issuing_transaction_ids=['5155165527080960'],
    status=approved,
    description=,
    metadata={},
    zip_code=01311-000,
    confirmed=2022-01-01 00:00:02,
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Getting a purchase

`GET /v2/issuing-purchase/:id`

Fetch a single purchase by its id. The "issuingTransactionIds" field links it to the ledger Issuing Transaction entries it generated.

**Request**

```python
import starkinfra

purchase = starkinfra.issuingpurchase.get("5715709195239424")

print(purchase)
```

**Response**

```python
IssuingPurchase(
    id=5715709195239424,
    holder_name=Tony Stark,
    product_id=654321,
    card_id=7834882116598271,
    card_ending=1234,
    purpose=purchase,
    installment_count=1,
    amount=5000,
    tax=0,
    issuer_amount=5000,
    issuer_currency_code=BRL,
    issuer_currency_symbol=R$,
    merchant_amount=5000,
    merchant_currency_code=BRL,
    merchant_currency_symbol=R$,
    merchant_category_code=fastFoodRestaurants,
    merchant_category_number=5814,
    merchant_category_type=food,
    merchant_country_code=BRA,
    acquirer_id=5656565656565656,
    merchant_id=5656565656565656,
    merchant_name=Stark Store,
    merchant_fee=0,
    wallet_id=None,
    method_code=chip,
    score=0.9775689825217906,
    end_to_end_id=79fcf95d-1dfb-40db-9fa8-679e329c58f0,
    tags=[],
    issuing_transaction_ids=['5155165527080960'],
    status=approved,
    description=,
    metadata={},
    zip_code=01311-000,
    confirmed=2022-01-01 00:00:02,
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Updating a purchase

`PATCH /v2/issuing-purchase/:id`

A purchase is settled by the network, so its financial fields are read-only. The only things you can change are its "description" (free text, up to 140 characters) and its "tags" — useful for reconciliation and bookkeeping. Each update creates an "updated" Log.

**Request**

```python
import starkinfra

purchase = starkinfra.issuingpurchase.update(
    id="5715709195239424",
    description="Reconciled with order #1234",
    tags=["reconciled", "month: january"]
)

print(purchase)
```

**Response**

```python
IssuingPurchase(
    id=5715709195239424,
    holder_name=Tony Stark,
    product_id=654321,
    card_id=7834882116598271,
    card_ending=1234,
    purpose=purchase,
    installment_count=1,
    amount=5000,
    tax=0,
    issuer_amount=5000,
    issuer_currency_code=BRL,
    issuer_currency_symbol=R$,
    merchant_amount=5000,
    merchant_currency_code=BRL,
    merchant_currency_symbol=R$,
    merchant_category_code=fastFoodRestaurants,
    merchant_category_number=5814,
    merchant_category_type=food,
    merchant_country_code=BRA,
    acquirer_id=5656565656565656,
    merchant_id=5656565656565656,
    merchant_name=Stark Store,
    merchant_fee=0,
    wallet_id=None,
    method_code=chip,
    score=0.9775689825217906,
    end_to_end_id=79fcf95d-1dfb-40db-9fa8-679e329c58f0,
    tags=['reconciled', 'month: january'],
    issuing_transaction_ids=['5155165527080960'],
    status=approved,
    description=Reconciled with order #1234,
    metadata={},
    zip_code=01311-000,
    confirmed=2022-01-01 00:00:02,
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Getting purchase Logs

`GET /v2/issuing-purchase/log`

Audit a purchase's history by querying its logs with the "purchaseIds" filter. A purchase that was approved, then confirmed, then edited returns one log per step. You can also filter by "types" (approved, denied, confirmed, reversed, voided, canceled, updated) and "after" / "before", or fetch a single log with GET /v2/issuing-purchase/log/:id.

**Request**

```python
import starkinfra

logs = starkinfra.issuingpurchase.log.query(
    limit=10,
    purchase_ids=["5715709195239424"]
)

for log in logs:
    print(log)
```

**Response**

```python
Log(
    id=6724771005489152,
    type=approved,
    purchase=IssuingPurchase(id=5715709195239424, amount=5000, status=approved, ...),
    created=2022-01-01 00:00:00
)

Log(
    id=5547499534213120,
    type=confirmed,
    purchase=IssuingPurchase(id=5715709195239424, amount=5000, status=confirmed, ...),
    created=2022-01-02 00:00:00
)
```

## Issuing Preview Installment Overview

An Issuing Preview Installment is a single upcoming installment of a card purchase that has not been billed yet. It exists only in the credit (postpaid) flow, where installment purchases are charged across several future Issuing Billing Invoices instead of being settled immediately.

Use it to preview, ahead of time, the amounts that will compose your future billing invoices — for example to forecast next month's charges or to reconcile what each purchase will add to each invoice.

Read-only. This resource is query-only: there is a single GET list endpoint. You cannot create, update or cancel a preview installment — Stark Infra derives them automatically from your installment purchases, and each one disappears from the list once its installment is actually billed.

amount vs. totalAmount. All money is in cents. amount is the value of this single installment; totalAmount is the full value of the source purchase. So a R$ 900.00 purchase split in 3 returns three preview installments of amount 30000 each, all sharing totalAmount 90000.

number and count. number is the position of this installment (1-based) and count is how many installments the source purchase was split into — e.g. number 2 of count 3.

source. Points back to the entity that originated the installment, formatted as "<resource>/<id>" — typically an Issuing Purchase, e.g. "issuing-purchase/5155165527080960".

due. The datetime when this installment is scheduled to be billed — i.e. the invoice it will land on.

### Querying Preview Installments

`GET /v2/issuing-preview-installment`

List the upcoming installments that have not been billed yet, in chunks of at most 100. Page through the results with the "cursor" returned in each response.

Sorting. Pass "sort" to order the results: "created" / "-created" by creation datetime, or "due" / "-due" by billing datetime. A leading "-" means descending. The default is "-created". Use "due" to walk the installments in the order they will be charged.

Filtering. Narrow the list with "after" / "before" (on the creation date), "ids" (fetch specific installments) and "tags". Use "limit" to cap the page size (max 100).

**Request**

```python
import starkinfra

installments = starkinfra.issuingpreviewinstallment.query(
    limit=10,
    sort="due"
)

for installment in installments:
    print(installment)
```

**Response**

```python
IssuingPreviewInstallment(
    id=6724771005489152,
    description=Stark Coffee Shop,
    amount=30000,
    total_amount=90000,
    source=issuing-purchase/5155165527080960,
    number=1,
    count=3,
    due=2022-02-01 00:00:00,
    tags=[],
    created=2022-01-01 00:00:00
)

IssuingPreviewInstallment(
    id=5547499534213120,
    description=Stark Coffee Shop,
    amount=30000,
    total_amount=90000,
    source=issuing-purchase/5155165527080960,
    number=2,
    count=3,
    due=2022-03-01 00:00:00,
    tags=[],
    created=2022-01-01 00:00:00
)
```

## Issuing Balance Overview

The Issuing Balance is the spendable wallet that backs every card you issue. Your cards draw on this single pool of funds — there is one Issuing Balance per workspace, not one per card or per holder.

Funding and draining. You top up the balance with an Issuing Invoice (pay its BR Code or transfer to its account) and pull money back out with an Issuing Withdrawal. Card purchases settle against the balance and lower it. Every one of these movements is recorded as an Issuing Transaction in the ledger — query those to see exactly how the balance reached its current amount.

amount is in cents. Like every monetary value in the API, amount is an integer in cents: 5000000 means R$50,000.00. It is the balance left after the latest transaction.

limit and maxLimit. Both report the ceiling your issuing wallet is allowed to reach. They are always equal (100000000, i.e. R$1,000,000.00, in the example below) and are not configurable through the API — treat them as read-only information, not a setting you can change.

### Getting your Balance

`GET /v2/issuing-balance`

Fetch your current Issuing Balance. There is a single balance per workspace, so this endpoint takes no id and returns just one entry — the wallet that all your cards spend from.

The updated field is the timestamp of the latest Issuing Transaction that moved the balance; if nothing has moved yet it reflects the current moment.

**Request**

```python
import starkinfra

balance = starkinfra.issuingbalance.get()

print(balance)
```

**Response**

```python
IssuingBalance(
    id=5715709195239424,
    amount=5000000,
    currency=BRL,
    updated=2022-01-01 00:00:00
)
```

## Issuing Invoice Overview

An Issuing Invoice is a Pix invoice you create to fund your issuing balance — the wallet your cards spend from. You must top this balance up before you can issue and spend on cards, so creating an invoice is usually the first call you make.

Creating an invoice returns a payable Pix: a "brcode" (the Pix copy-and-paste / QR string), a "link" to a hosted payment page and a "due" datetime (7 days ahead by default). Pay it from any account, and once Stark Infra confirms the payment the invoice moves to "paid" and the amount lands in your Issuing Balance, ready to spend on Issuing Cards.

amount is always in cents — 10000 means R$100.00. It must be a positive integer.

The returned taxId is the issuer's, not the payer's. You may pass a payer "name" and "taxId" when creating the invoice (they default to your workspace owner's if omitted), but the "taxId" returned on the invoice is always the tax id of your card-issuer entity — the party receiving the funds.

Watch for the "paid" status. The deposit is asynchronous: the invoice is born "created" and only becomes "paid" once the Pix settles. Track that transition through the invoice Logs or a webhook subscribed to issuing-invoice events — that is your signal that the balance was funded.

### Invoice Statuses

Each Issuing Invoice has a status that reflects where it is in its life cycle:

| Status | Description |
| --- | --- |
| created | The invoice was created and is waiting for payment. Its brcode and link are payable. |
| paid | The Pix was paid and the amount was credited to your issuing balance. This is the status you watch for. |
| overdue | The due datetime passed and the invoice was not paid yet. It can still be paid for a short grace period. |
| expired | The invoice expired and can no longer be paid. Create a new one to fund the balance. |
| canceled | The invoice was canceled and can no longer be paid. |

### Invoice Logs

Every time an Issuing Invoice changes, we create a Log entry. Logs are how you follow an invoice from creation to payment without polling — query them, or subscribe a webhook to issuing-invoice events, and react to the "paid" log to know the funds landed in your balance.

Query the invoice logs with GET /v2/issuing-invoice/log (filter by ids, types, after / before), or fetch a single one with GET /v2/issuing-invoice/log/:id. Log types are: created, paid, overdue, expired, credited and canceled. The "invoice" field in each log is an abbreviated representation of the Issuing Invoice as it was at that point (shown abbreviated with "...").

### Creating an Invoice

`POST /v2/issuing-invoice`

Create an invoice to fund your issuing balance by sending an "amount" in cents. "name", "taxId", "tags" and "metadata" are optional — if you omit "name" / "taxId" they default to your workspace owner's.

The response carries the payable Pix: "brcode", "link" and "due". The invoice starts as "created"; "issuingTransactionId" stays null until the Pix is paid.

NOTE: You cannot create a funding invoice while you have an unpaid Issuing Billing Invoice — if a billing invoice is pending or overdue, settle it first.

NOTE: In Sandbox, an invoice has about a 90% chance of being paid automatically by our simulation system within 10 minutes — handy for testing the funding flow end to end.

**Request**

```python
import starkinfra

invoice = starkinfra.issuinginvoice.create(
    starkinfra.IssuingInvoice(
        amount=10000,
        name="Tony Stark",
        tax_id="012.345.678-90",
        tags=["department: tech"]
    )
)

print(invoice)
```

**Response**

```python
IssuingInvoice(
    id=5715709195239424,
    amount=10000,
    tax_id=20.018.183/0001-80,
    name=Tony Stark,
    tags=['department: tech'],
    metadata={},
    status=created,
    brcode=00020101021226870014br.gov.bcb.pix2565...,
    due=2022-02-10 00:00:00,
    link=https://sandbox.api.starkinfra.com/invoicelink/5715709195239424,
    issuing_transaction_id=None,
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Listing Invoices

`GET /v2/issuing-invoice`

List your invoices in pages of at most 100. Filter by "status" (created, paid, overdue, expired, canceled), "tags" and "after" / "before". Use the returned "cursor" to page through results.

**Request**

```python
import starkinfra

invoices = starkinfra.issuinginvoice.query(
    limit=10,
    status="paid"
)

for invoice in invoices:
    print(invoice)
```

**Response**

```python
IssuingInvoice(
    id=5715709195239424,
    amount=10000,
    tax_id=20.018.183/0001-80,
    name=Tony Stark,
    tags=['department: tech'],
    metadata={},
    status=paid,
    brcode=00020101021226870014br.gov.bcb.pix2565...,
    due=2022-02-10 00:00:00,
    link=https://sandbox.api.starkinfra.com/invoicelink/5715709195239424,
    issuing_transaction_id=6306924158455808,
    created=2022-01-01 00:00:00,
    updated=2022-01-02 00:00:00
)
```

### Getting an Invoice

`GET /v2/issuing-invoice/:id`

Fetch a single invoice by its id — for example to re-read its current "status" or recover its "brcode" and "link" to present the Pix again.

**Request**

```python
import starkinfra

invoice = starkinfra.issuinginvoice.get("5715709195239424")

print(invoice)
```

**Response**

```python
IssuingInvoice(
    id=5715709195239424,
    amount=10000,
    tax_id=20.018.183/0001-80,
    name=Tony Stark,
    tags=['department: tech'],
    metadata={},
    status=created,
    brcode=00020101021226870014br.gov.bcb.pix2565...,
    due=2022-02-10 00:00:00,
    link=https://sandbox.api.starkinfra.com/invoicelink/5715709195239424,
    issuing_transaction_id=None,
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Getting an Invoice's Logs

`GET /v2/issuing-invoice/log`

Follow an invoice's life cycle by querying its logs. Filter by "types" (e.g. "paid") to react only to the transitions you care about, or by "ids" to scope to specific invoices, plus "after" / "before". Fetch a single log with GET /v2/issuing-invoice/log/:id.

**Request**

```python
import starkinfra

logs = starkinfra.issuinginvoice.log.query(
    ids=["5715709195239424"]
)

for log in logs:
    print(log)
```

**Response**

```python
Log(
    id=6724771005489152,
    type=created,
    invoice=IssuingInvoice(id=5715709195239424, amount=10000, status=created, ...),
    created=2022-01-01 00:00:00
)
Log(
    id=5547499534213120,
    type=paid,
    invoice=IssuingInvoice(id=5715709195239424, amount=10000, status=paid, ...),
    created=2022-01-02 00:00:00
)
```

## Issuing Billing Invoice Overview

An Issuing Billing Invoice is the postpaid settlement document of a credit issuing operation. At the close of each billing period Stark Infra consolidates everything your cards spent and bills it back to you as a single Pix invoice that you pay.

It is composed of Issuing Billing Transaction entries. Each purchase, withdrawal, refund and reversal settled in the period becomes a billing transaction; the invoice's nominalAmount is the sum of those entries. Query the Issuing Billing Transaction section to see the line items behind an invoice.

Read-only. You do not create or update billing invoices — Stark Infra generates and closes them on the billing cycle. The only operations available here are listing them and fetching one by id.

Pay it before funding. While an invoice is "pending" or "overdue" you cannot create a funding Issuing Invoice — settle the outstanding billing invoice first. Pay it with its brcode (a Pix BR Code) or by opening its link.

Money direction & amounts. nominalAmount is the original amount owed for the period; amount is what you owe right now — equal to nominalAmount until the due date, then growing by fine and interest once the invoice is overdue. Both are integers in cents (e.g. 100000 = R$ 1,000.00). fine is a one-off percentage and interest a monthly percentage, applied only when you pay after the due date.

### Billing Invoice Statuses

Each Issuing Billing Invoice has a status that reflects where it is in the billing cycle:

| Status | Description |
| --- | --- |
| created | The invoice was generated for the billing period but is not yet open for payment. |
| pending | The invoice is open and awaiting payment. Pay it with its brcode or link before the due date to avoid fine and interest. |
| overdue | The due date passed without payment. The amount now grows by fine and interest until it is paid. |
| paid | The invoice was settled. No further amount is owed for the period. |
| expired | The invoice was not paid and is no longer payable through this document. The outstanding balance is handled outside this flow. |

### Listing Billing Invoices

`GET /v2/issuing-billing-invoice`

List your billing invoices in chunks of at most 100. Filter by "status" (created, pending, overdue, expired, paid), by "after" / "before" on the creation date, by "ids" or by "tags".

Use "status=pending" to find what you currently owe — these are the invoices that must be paid before you can fund your operation with a new Issuing Invoice.

**Request**

```python
import starkinfra

billing_invoices = starkinfra.issuingbillinginvoice.query(
    limit=10,
    status="pending"
)

for billing_invoice in billing_invoices:
    print(billing_invoice)
```

**Response**

```python
IssuingBillingInvoice(
    id=5715709195239424,
    name=Tony Stark,
    tax_id=012.345.678-90,
    fine=2.0,
    interest=1.0,
    status=pending,
    amount=100000,
    nominal_amount=100000,
    brcode=00020101021226930014br.gov.bcb.pix2571brcode-h.sandbox.starkinfra.com/v2/d6a916beba1c41a5bbe31d63a93553c35204000053039865802BR5925Stark Bank S.A.6009Sao Paulo62070503***6304811B,
    link=https://invoice-h.sandbox.starkbank.com/invoicelink/851ff545535c40ba88e24a05accb9978,
    due=2022-02-10 00:00:00,
    start=2022-01-01 00:00:00,
    end=2022-01-31 23:59:59,
    created=2022-02-01 00:00:00,
    updated=2022-02-01 00:00:00
)
```

### Getting a Billing Invoice

`GET /v2/issuing-billing-invoice/:id`

Fetch a single billing invoice by its id to read its current amount, status, due date and the brcode / link you use to pay it.

The billing period the invoice covers is given by "start" and "end"; "due" is when payment is expected. Once overdue, "amount" reflects the original "nominalAmount" plus the accrued fine and interest.

**Request**

```python
import starkinfra

billing_invoice = starkinfra.issuingbillinginvoice.get("5715709195239424")

print(billing_invoice)
```

**Response**

```python
IssuingBillingInvoice(
    id=5715709195239424,
    name=Tony Stark,
    tax_id=012.345.678-90,
    fine=2.0,
    interest=1.0,
    status=overdue,
    amount=103000,
    nominal_amount=100000,
    brcode=00020101021226930014br.gov.bcb.pix2571brcode-h.sandbox.starkinfra.com/v2/d6a916beba1c41a5bbe31d63a93553c35204000053039865802BR5925Stark Bank S.A.6009Sao Paulo62070503***6304811B,
    link=https://invoice-h.sandbox.starkbank.com/invoicelink/851ff545535c40ba88e24a05accb9978,
    due=2022-02-10 00:00:00,
    start=2022-01-01 00:00:00,
    end=2022-01-31 23:59:59,
    created=2022-02-01 00:00:00,
    updated=2022-02-15 00:00:00
)
```

## Issuing Billing Transaction Overview

An Issuing Billing Transaction is a single line entry that composes an Issuing Billing Invoice. Each purchase, withdrawal or fee that was billed in a period produces one transaction — and an installment purchase produces one transaction per installment that falls in that period.

This is a read-only, query-only resource: there is one endpoint and it lists transactions. You never create, update or cancel a transaction directly — they are generated by Stark Infra as purchases settle into invoices.

Reconciling an invoice. Pass "invoiceId" to list every transaction that makes up a given Issuing Billing Invoice. The transactions are ordered as they were applied, and each one carries the running "balance" — the invoice balance in cents after that transaction — so the last transaction's balance equals the invoice total.

Tracing back to the purchase. The "source" field points to the entity that originated the transaction, formatted as "/" (e.g. "issuing-purchase/5155165527080960"). Use it to walk from a billed line back to the originating Issuing Purchase. The "externalId" is the unique external identifier of the transaction (for an installment it is suffixed with the installment number, e.g. "5155165527080960-1").

### Installments

When a purchase is split into installments, each installment is billed as its own transaction. "installmentCount" is the total number of installments of the source purchase and "installment" is the number of this one (1-based). A single-payment purchase has "installment": 1 and "installmentCount": 1.

The transactions for the different installments of the same purchase share the same originating "source", but each lands in the invoice of its own billing period — so to see them all you query across invoices, not within a single one.

### Amounts and currency

All amounts are integers in cents. "amount" is what was billed in BRL (e.g. 30000 = R$ 300.00) and "tax" is the IOF in cents applied to the transaction. For international transactions, "merchantAmount" and "merchantCurrencyCode" hold the amount in the merchant's currency and "rate" is the conversion rate that was applied; for domestic transactions the merchant amount equals the amount, the code is "BRL" and the rate is 1.0.

### Listing Billing Transactions

`GET /v2/issuing-billing-transaction`

List billing transactions in pages of up to 100 ("limit"). The response wraps the results in a "transactions" list plus a "cursor" — pass the cursor back to fetch the next page; a null cursor means there are no more results.

Pass "invoiceId" to scope the list to a single Issuing Billing Invoice and reconcile it to its line entries. You can also filter by "ids" (a comma-separated list of specific transaction ids), by "tags", and by "after" / "before" to bound the creation date.

**Request**

```python
import starkinfra

transactions = starkinfra.issuingbillingtransaction.query(
    limit=10,
    invoice_id="5715709195239424"
)

for transaction in transactions:
    print(transaction)
```

**Response**

```python
IssuingBillingTransaction(
    id=5715709195239424,
    installment=1,
    installment_count=3,
    amount=30000,
    balance=60000,
    source=issuing-purchase/5155165527080960,
    external_id=5155165527080960-1,
    description=Stark Coffee Shop,
    card_ending=1234,
    holder_name=Tony Stark,
    tax=0,
    rate=1.0,
    merchant_amount=30000,
    merchant_currency_code=BRL,
    created=2022-01-01 00:00:00
)
```

## Issuing Withdrawal Overview

An Issuing Withdrawal moves funds out of your Issuing Balance and back into your Stark Infra workspace. It is the inverse of a funding Issuing Invoice: where an invoice tops your issuing balance up, a withdrawal drains the excess back to your main account.

Use a withdrawal whenever your issuing balance holds more than you need to back your cards — the money returns to your regular Stark Infra balance, where it can be transferred out or reused.

Two transaction ids. A successful withdrawal records both legs of the move: issuingTransactionId is the debit on your issuing balance, and transactionId is the matching credit on your Stark Infra workspace balance. Both are returned only after the withdrawal settles.

externalId (idempotency). You assign your own externalId to each withdrawal. It must be unique per workspace — sending a withdrawal whose externalId was already used is rejected, so a retried request never withdraws twice. Use it later to look a withdrawal back up with the externalIds filter.

amount is an integer in cents (e.g. 10000 means R$100.00) and must be positive. description is mandatory and must be 10 to 200 characters long.

### Creating a Withdrawal

`POST /v2/issuing-withdrawal`

Create a withdrawal by sending amount, externalId and description. The amount is debited from your issuing balance and credited back to your Stark Infra workspace balance.

The externalId must be unique per workspace — a repeated externalId is rejected, which makes a retried request safe. The description is mandatory and must be 10 to 200 characters.

The response echoes the withdrawal together with the transactionId (workspace credit) and issuingTransactionId (issuing-balance debit) recorded for the move.

**Request**

```python
import starkinfra

withdrawal = starkinfra.issuingwithdrawal.create(
    starkinfra.IssuingWithdrawal(
        amount=10000,
        external_id="withdrawal-2022-001",
        description="Excess funds withdrawal",
        tags=["tony", "stark"]
    )
)

print(withdrawal)
```

**Response**

```python
IssuingWithdrawal(
    id=5715709195239424,
    amount=10000,
    external_id=withdrawal-2022-001,
    description=Excess funds withdrawal,
    transaction_id=6724771005489152,
    issuing_transaction_id=7834882116598271,
    tags=['tony', 'stark'],
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Listing Withdrawals

`GET /v2/issuing-withdrawal`

List your withdrawals in pages of up to 100. Filter by "after" / "before" (creation date), "tags", or "externalIds" to find the ones you assigned.

The response is paginated: follow the "cursor" to fetch the next page (null means the last page).

**Request**

```python
import starkinfra

withdrawals = starkinfra.issuingwithdrawal.query(
    limit=10,
    after="2022-01-01",
    before="2022-12-31",
    tags=["tony", "stark"]
)

for withdrawal in withdrawals:
    print(withdrawal)
```

**Response**

```python
IssuingWithdrawal(
    id=5715709195239424,
    amount=10000,
    external_id=withdrawal-2022-001,
    description=Excess funds withdrawal,
    transaction_id=6724771005489152,
    issuing_transaction_id=7834882116598271,
    tags=['tony', 'stark'],
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Getting a Withdrawal

`GET /v2/issuing-withdrawal/:id`

Fetch a single withdrawal by its id.

**Request**

```python
import starkinfra

withdrawal = starkinfra.issuingwithdrawal.get("5715709195239424")

print(withdrawal)
```

**Response**

```python
IssuingWithdrawal(
    id=5715709195239424,
    amount=10000,
    external_id=withdrawal-2022-001,
    description=Excess funds withdrawal,
    transaction_id=6724771005489152,
    issuing_transaction_id=7834882116598271,
    tags=['tony', 'stark'],
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

## Issuing Transaction Overview

An Issuing Transaction is a single entry in the ledger of your Issuing Balance. Every event that moves money in your issuing account — a card purchase, an Issuing Invoice that funds it, an Issuing Withdrawal that takes money out, or a fee — produces exactly one transaction.

Read-only. You never create, update or delete transactions: Stark Infra writes them automatically as funds move. You can only list them and fetch them by id.

Amount sign. The amount is in cents and its sign tells you the direction of the money: a positive amount is a credit (money in — e.g. an invoice that tops up your balance), a negative amount is a debit (money out — e.g. a purchase or a withdrawal).

Running balance. The balance field is your full issuing balance, in cents, right after this transaction was applied. Walking the transactions in order lets you reconcile every cent of your Issuing Balance without recomputing it yourself.

Source. The source links a transaction back to the entity that generated it, in the form resource/id — for example issuingPurchase/6724771005489152. Use it to trace a balance change back to the Issuing Purchase, Issuing Invoice or Issuing Withdrawal that caused it, and as a query filter to pull every transaction tied to one source.

### Listing Transactions

`GET /v2/issuing-transaction`

List your issuing transactions, most recent first, in pages of at most 100. Narrow the result with "after" / "before" (by creation date), "tags", "ids" or "externalIds".

Filter by "source" to pull every transaction generated by one entity — pass the full resource/id value (e.g. "issuingPurchase/6724771005489152") to follow a single purchase, invoice or withdrawal through the ledger.

**Request**

```python
import starkinfra

transactions = starkinfra.issuingtransaction.query(
    after="2022-01-01",
    before="2022-12-31"
)

for transaction in transactions:
    print(transaction)
```

**Response**

```python
IssuingTransaction(
    id=5715709195239424,
    amount=-1500,
    balance=5000000,
    description=Issuing purchase 6724771005489152,
    source=issuingPurchase/6724771005489152,
    tags=['tony', 'stark'],
    created=2022-01-01 00:00:00
)
IssuingTransaction(
    id=5189683645874176,
    amount=5001500,
    balance=5001500,
    description=Issuing invoice 5547499534213120,
    source=issuingInvoice/5547499534213120,
    tags=[],
    created=2021-12-31 00:00:00
)
```

### Getting a Transaction

`GET /v2/issuing-transaction/:id`

Fetch a single transaction by its id to inspect one ledger entry — its amount, the resulting balance and the source that generated it.

**Request**

```python
import starkinfra

transaction = starkinfra.issuingtransaction.get("5715709195239424")

print(transaction)
```

**Response**

```python
IssuingTransaction(
    id=5715709195239424,
    amount=-1500,
    balance=5000000,
    description=Issuing purchase 6724771005489152,
    source=issuingPurchase/6724771005489152,
    tags=['tony', 'stark'],
    created=2022-01-01 00:00:00
)
```

## Issuing Product Overview

An Issuing Product is the card product — the BIN range — that every Issuing Card references through its productId. It defines the network, funding type, holder type and code shared by every card created under it.

Read-only. Products are not created through the API: they are defined together with your account manager when you set up card issuing. The API exposes a single endpoint to list the products available to your workspace so you can pick a productId to issue cards with.

Getting a productId. List your products with the endpoint below and copy the id of the one you want. If you don't have any product yet, request one at help@starkinfra.com.

Field meanings. network is the card network ("Visa" or "Mastercard"). fundingType is how the card settles — "prepaid" (balance is debited from your Issuing Balance up front) or "credit". holderType tells whether the product issues cards to a "business" or to an "individual". code is the product code that identifies the specific card product.

NOTE: settlement, client and customerType are legacy aliases kept for backward compatibility — settlement mirrors fundingType, while client and customerType both mirror holderType. Prefer the canonical fields in new integrations.

### Listing your Products

`GET /v2/issuing-product`

List the card products available to your workspace and copy the id of the one you want to issue cards with. Each entry exposes the product's network, fundingType, holderType and code.

This is the only Issuing Product endpoint — products are read-only and cannot be created, updated or deleted through the API.

**Request**

```python
import starkinfra

products = starkinfra.issuingproduct.query()

for product in products:
    print(product)
```

**Response**

```python
IssuingProduct(
    id=5715709195239424,
    network=Mastercard,
    funding_type=credit,
    holder_type=individual,
    code=ABC,
    created=2022-01-01 00:00:00
)
```

## Issuing Design Overview

An Issuing Design is the artwork template applied when a physical card is embossed and printed. It defines the visual layout — the artwork, fonts and positioning — that the embosser prints onto the card.

Designs are read-only: you don't create them through the API. They are registered for your sub-issuer ahead of time (together with the embossers that can print them), and you reference one by its id when you order a physical card through an Issuing Embossing Kit.

Registering a design requires network approval. A design is not something you configure on your own: each one must be approved by the card network (Visa/Mastercard) through the embosser that will print it before it can be registered for your sub-issuer. To set up a new design or change an existing one, contact help@starkinfra.com and our team will walk you through the network approval and embosser configuration.

Picking a design for a kit. Each design lists the embosserIds that are able to print it. When you assemble an Issuing Embossing Kit, the card design and the envelope design you choose must both be printable by the same embosser — so check the embosserIds overlap before referencing them.

Previewing the artwork. Beyond the design metadata, you can download a PDF proof of the layout to preview exactly how a card or envelope will look once printed. See Getting a Design's PDF below.

### Design Types

A design's "type" is one of exactly two values, telling you which part of the physical mailing it applies to:

| Type | Description |
| --- | --- |
| card | Artwork printed on the plastic card itself. |
| envelope | Artwork for any printed material that is not the plastic card — the mailing envelope, a flyer, a leaflet or any other paper that ships alongside the card. Anything other than the card is typed as "envelope". |

### Listing Designs

`GET /v2/issuing-design`

List the designs available to your sub-issuer. Use this to discover which design ids you can reference when ordering physical cards, and to read each design's type and the embosserIds that can print it.

Filter by "ids" to fetch specific designs, and page through results with "limit" (max 100) and "cursor" — our SDKs handle the cursor for you.

**Request**

```python
import starkinfra

designs = starkinfra.issuingdesign.query(limit=10)

for design in designs:
    print(design)
```

**Response**

```python
IssuingDesign(
    id=5715709195239424,
    name=Standard Blue,
    embosser_ids=['6724771005489152'],
    type=card,
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Getting a Design

`GET /v2/issuing-design/:id`

Fetch a single design by its id to read its name, type and the embosserIds that support it.

**Request**

```python
import starkinfra

design = starkinfra.issuingdesign.get("5715709195239424")

print(design)
```

**Response**

```python
IssuingDesign(
    id=5715709195239424,
    name=Standard Blue,
    embosser_ids=['6724771005489152'],
    type=card,
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Getting a Design's PDF

`GET /v2/issuing-design/:id/pdf`

Download a PDF proof of the design's artwork to preview how the card or envelope will look once printed.

Unlike the other endpoints, this one returns the raw PDF bytes — there is no JSON body. With curl, write the response straight to a file with --output design.pdf; our SDKs return the binary content for you to save.

**Request**

```python
import starkinfra

pdf = starkinfra.issuingdesign.pdf("5715709195239424")

with open("design.pdf", "wb") as f:
    f.write(pdf)
```

**Response**

```python
b'%PDF-1.4 ...' # Binary PDF content
```

## Issuing Embossing Kit Overview

An Issuing Embossing Kit bundles the physical materials used to personalize a card — the plastic and packaging — together with the Issuing Designs available for embossing.

Where it fits. When you order an Issuing Embossing Request for a physical card, you reference a kitId. The kit determines which designs you can pick and how the card is physically produced and shipped, so you list the available kits first and reuse their ids in your embossing requests.

Read-only. Kits are provisioned for your sub-issuer by Stark Infra — you cannot create, change or delete them. The only operations are listing the kits available to you and fetching one by its id.

The designs field. Each kit returns a "designs" list referencing the Issuing Designs it supports. These come back as id-only objects (e.g. {"id": "6284441486065664"}) — resolve a design's full details through the Issuing Design section using that id.

### Listing your Embossing Kits

`GET /v2/issuing-embossing-kit`

List the embossing kits available to your sub-issuer. Use the returned ids when ordering an Issuing Embossing Request.

Filter by "designIds" to keep only the kits that support a given Issuing Design (up to 30 ids), by "ids" to fetch specific kits, or by "after" / "before" to bound the results by creation date.

**Request**

```python
import starkinfra

kits = starkinfra.issuingembossingkit.query(limit=10)

for kit in kits:
    print(kit)
```

**Response**

```python
IssuingEmbossingKit(
    id=5715709195239424,
    name=Standard Kit,
    designs=[
        IssuingDesign(
            id=6284441486065664,
            name=None,
            embosser_ids=None,
            type=None,
            created=None,
            updated=None
        ),
        IssuingDesign(
            id=6284441486065665,
            name=None,
            embosser_ids=None,
            type=None,
            created=None,
            updated=None
        )
    ],
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Getting an Embossing Kit

`GET /v2/issuing-embossing-kit/:id`

Fetch a single embossing kit by its id to inspect its name and the Issuing Designs it supports.

**Request**

```python
import starkinfra

kit = starkinfra.issuingembossingkit.get("5715709195239424")

print(kit)
```

**Response**

```python
IssuingEmbossingKit(
    id=5715709195239424,
    name=Standard Kit,
    designs=[
        IssuingDesign(
            id=6284441486065664,
            name=None,
            embosser_ids=None,
            type=None,
            created=None,
            updated=None
        ),
        IssuingDesign(
            id=6284441486065665,
            name=None,
            embosser_ids=None,
            type=None,
            created=None,
            updated=None
        )
    ],
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

## Issuing Stock Overview

An Issuing Stock tracks the raw blank cards available for embossing. Each stock entry pairs one card design (Issuing Design) with the embosser that physically holds those blanks, and its balance tells you how many blanks are still available to be embossed.

Read-only. You never create or edit a stock directly — Stark Infra opens one for each design/embosser pair you have. You can only Query it, Get it by id, and read its Logs.

Raising the balance. To add more blanks, register an Issuing Restock: each restock increases the matching stock's balance by the ordered amount once the blanks arrive at the embosser.

Low-balance alerts. Embossing a Issuing Embossing Request consumes blanks and lowers the balance. Set an Issuing Stock Rule to be warned (via webhook) before a stock runs out, so you can restock in time.

### Stock Balance

The balance is the count of card blanks currently available for embossing. It is computed on demand, so it is not returned by default — inform expand=balance on Query or Get to include it.

Without expand=balance a stock returns only its identifiers (id, designId, embosserId, embosserName) and timestamps.

### Stock Logs

Every time a stock's balance shifts, Stark Infra creates a Log entry. Logs let you audit how a stock reached its current balance — each restock that came in and each batch of blanks that was embossed or written off.

Each log carries a top-level count (the signed shift in balance for that event) and a nested stock object (a representation of the stock the log refers to). The log type is one of:

| Type | Description |
| --- | --- |
| created | The stock was opened for a design/embosser pair, seeding its initial balance. |
| restocked | An Issuing Restock arrived at the embosser and raised the balance. |
| spent | Blanks were consumed to emboss cards, lowering the balance. |
| lost | Blanks were written off (damaged or otherwise unusable), lowering the balance. |

Query the stock logs with GET /v2/issuing-stock/log (filter by stockIds, types, after / before), or fetch a single one with GET /v2/issuing-stock/log/:id.

### Querying Stocks

`GET /v2/issuing-stock`

List your stocks, newest first, in pages of up to 100. Filter by "designIds", "embosserIds", "ids" or by creation date with "after" / "before".

Inform "expand=balance" to include each stock's current blank count — it is omitted otherwise (see the Stock Balance note above).

**Request**

```python
import starkinfra

stocks = starkinfra.issuingstock.query(limit=10)

for stock in stocks:
    print(stock)
```

**Response**

```python
IssuingStock(
    id=5715709195239424,
    design_id=6724771005489152,
    embosser_id=7834882116598271,
    embosser_name=Stark Embosser,
    balance=500,
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Getting a Stock

`GET /v2/issuing-stock/:id`

Fetch a single stock by its id. As with Query, inform "expand=balance" to include its current blank count.

**Request**

```python
import starkinfra

stock = starkinfra.issuingstock.get("5715709195239424")

print(stock)
```

**Response**

```python
IssuingStock(
    id=5715709195239424,
    design_id=6724771005489152,
    embosser_id=7834882116598271,
    embosser_name=Stark Embosser,
    balance=500,
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Getting a Stock's Logs

`GET /v2/issuing-stock/log`

Audit a stock's balance history by querying its logs with the "stockIds" filter. Each balance shift creates a Log — a stock that was created, restocked and then partly embossed returns one log per event. The "stock" field in each log is a representation of the Issuing Stock the log refers to.

You can also filter by "types" (created, restocked, spent, lost) and "after" / "before", or fetch a single log with GET /v2/issuing-stock/log/:id.

**Request**

```python
import starkinfra

logs = starkinfra.issuingstock.log.query(stock_ids=["5715709195239424"])

for log in logs:
    print(log)
```

**Response**

```python
Log(
    id=6724771005489152,
    type=created,
    stock=IssuingStock(id=5715709195239424, balance=500, ...),
    count=500,
    created=2022-01-01 00:00:00
)
Log(
    id=5547499534213120,
    type=restocked,
    stock=IssuingStock(id=5715709195239424, balance=1500, ...),
    count=1000,
    created=2022-02-01 00:00:00
)
Log(
    id=5189683645874176,
    type=spent,
    stock=IssuingStock(id=5715709195239424, balance=1450, ...),
    count=-50,
    created=2022-03-01 00:00:00
)
```

## Issuing Stock Rule Overview

An Issuing Stock Rule sets a low-balance alert on one of your Issuing Stock entries. When that stock's available count drops below the rule's minimumBalance, we notify the contacts you registered so you can reorder cards before you run out.

One rule per stock. Each stock can have a single active rule at a time. The rule is tied to a stockId at creation, and that link cannot be changed afterwards — to point the alert at a different stock, cancel this rule and create a new one.

Who gets notified. A rule notifies the addresses in its "emails" list and the numbers in its "phones" list (each capped at 10). At least one email or phone must be present — a rule with no contacts is rejected.

What triggers it. minimumBalance is a stock count, not money: it is the number of remaining cards below which the alert fires. It must be a positive integer.

### Stock Rule Statuses

Each Issuing Stock Rule has a status that reflects where it is in its life cycle:

| Status | Description |
| --- | --- |
| active | The rule is active and monitoring its stock. New rules are created in this status. Only one active rule is allowed per stock. |
| canceled | The rule was permanently canceled and no longer fires alerts. This is irreversible. |

### Creating a Stock Rule

`POST /v2/issuing-stock-rule`

Create one or more stock rules by sending a "rules" list. Each rule needs a minimumBalance and the stockId of the Issuing Stock it watches, plus at least one contact across "emails" and "phones".

Creating a second rule for a stock that already has an active rule is rejected — cancel the existing one first.

**Request**

```python
import starkinfra

rules = starkinfra.issuingstockrule.create([
    starkinfra.IssuingStockRule(
        minimum_balance=1000,
        stock_id="6724771005489152",
        emails=["aria@stark.com"],
        phones=["+5511999999999"],
        tags=["stock: black-card"]
    )
])

for rule in rules:
    print(rule)
```

**Response**

```python
IssuingStockRule(
    id=5715709195239424,
    minimum_balance=1000,
    stock_id=6724771005489152,
    emails=['aria@stark.com'],
    phones=['+5511999999999'],
    tags=['stock: black-card'],
    status=active,
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Querying Stock Rules

`GET /v2/issuing-stock-rule`

List your stock rules in chunks of at most 100. Filter by "status", "stockIds" (to find the rule watching a given stock), "ids", "tags" and "after" / "before".

**Request**

```python
import starkinfra

rules = starkinfra.issuingstockrule.query(
    limit=10,
    status="active"
)

for rule in rules:
    print(rule)
```

**Response**

```python
IssuingStockRule(
    id=5715709195239424,
    minimum_balance=1000,
    stock_id=6724771005489152,
    emails=['aria@stark.com'],
    phones=['+5511999999999'],
    tags=['stock: black-card'],
    status=active,
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Updating a Stock Rule

`PATCH /v2/issuing-stock-rule/:id`

Change a rule's minimumBalance, emails, phones or tags. The stockId is fixed at creation and cannot be updated.

Each list you send replaces the old one wholesale, and the combined emails + phones must still hold at least one contact. A canceled rule cannot be updated.

**Request**

```python
import starkinfra

rule = starkinfra.issuingstockrule.update(
    "5715709195239424",
    minimum_balance=2000,
    emails=["aria@stark.com", "tony@stark.com"]
)

print(rule)
```

**Response**

```python
IssuingStockRule(
    id=5715709195239424,
    minimum_balance=2000,
    stock_id=6724771005489152,
    emails=['aria@stark.com', 'tony@stark.com'],
    phones=['+5511999999999'],
    tags=['stock: black-card'],
    status=active,
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Canceling a Stock Rule

`DELETE /v2/issuing-stock-rule/:id`

Permanently cancel a rule by its id. Its status becomes "canceled" and it stops firing alerts. This action is irreversible, and once canceled the stock is free to receive a new active rule.

**Request**

```python
import starkinfra

rule = starkinfra.issuingstockrule.cancel("5715709195239424")

print(rule)
```

**Response**

```python
IssuingStockRule(
    id=5715709195239424,
    minimum_balance=2000,
    stock_id=6724771005489152,
    emails=['aria@stark.com', 'tony@stark.com'],
    phones=['+5511999999999'],
    tags=['stock: black-card'],
    status=canceled,
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

## Issuing Restock Overview

An Issuing Restock is an order you place to replenish your Issuing Stock of blank cards. Each physical card you emboss consumes one blank from that stock — when it runs low, create a Restock to request more.

What you send. A restock needs only a count (how many blanks to order) and the stockId of the Issuing Stock to top up. You can create up to 100 restocks in a single request by sending more entries in the "restocks" list.

Fulfillment is asynchronous. Creating a restock does not add blanks to your stock immediately. The order starts as "created", moves to "processing" while it is being fulfilled, and reaches "confirmed" once the blanks have been added to your stock. You don't change a restock's status yourself — it advances on its own and is reflected back to you through its status and its Logs.

Read-only after creation. A restock has no update or cancel endpoint. Once placed, you can only query it, fetch it by id, or follow its Logs to track fulfillment.

### Restock Statuses

Each Issuing Restock has a status that reflects where its order is in the fulfillment flow:

| Status | Description |
| --- | --- |
| created | The restock order was registered and is waiting to be fulfilled. |
| processing | The order is being fulfilled — the requested blanks are being added to your stock. |
| confirmed | The order was fulfilled: the blanks are now part of your stock and available for embossing. |
| canceled | The order was canceled and its blanks were not added to your stock. |

### Restock Logs

Every time a restock advances through its fulfillment flow, we create a Log entry. Logs let you follow an order from placement to confirmation without polling the restock itself — you get one log per step the order goes through (created, processing, confirmed).

Query the restock logs with GET /v2/issuing-restock/log (filter by restockIds, types, after / before), or fetch a single one with GET /v2/issuing-restock/log/:id. Each log carries the "restock" it refers to (shown abbreviated with "...").

### Creating a Restock

`POST /v2/issuing-restock`

Order blank cards by sending a "restocks" list. Each entry needs a count (the number of blanks to order) and the stockId of the Issuing Stock to replenish. Optionally tag each restock for later queries.

New restocks come back with status "created" — they are queued for fulfillment, not yet added to your stock.

**Request**

```python
import starkinfra

restocks = starkinfra.issuingrestock.create([
    starkinfra.IssuingRestock(
        count=100,
        stock_id="5715709195239424",
        tags=["department: tech"]
    )
])

for restock in restocks:
    print(restock)
```

**Response**

```python
IssuingRestock(
    id=6724771005489152,
    count=100,
    stock_id=5715709195239424,
    status=created,
    tags=['department: tech'],
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Querying Restocks

`GET /v2/issuing-restock`

List your restocks in pages of up to 100. Narrow the results with "status", "stockIds", "ids", "tags" and the "after" / "before" date filters.

For example, filter by "status=created" to find orders still awaiting fulfillment, or by a "stockIds" value to see every restock placed against a given stock.

**Request**

```python
import starkinfra

restocks = starkinfra.issuingrestock.query(status="created")

for restock in restocks:
    print(restock)
```

**Response**

```python
IssuingRestock(
    id=6724771005489152,
    count=100,
    stock_id=5715709195239424,
    status=created,
    tags=['department: tech'],
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Getting a Restock

`GET /v2/issuing-restock/:id`

Fetch a single restock by its id to check its current status and count.

**Request**

```python
import starkinfra

restock = starkinfra.issuingrestock.get("6724771005489152")

print(restock)
```

**Response**

```python
IssuingRestock(
    id=6724771005489152,
    count=100,
    stock_id=5715709195239424,
    status=confirmed,
    tags=['department: tech'],
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Getting a Restock's Logs

`GET /v2/issuing-restock/log`

Follow an order's fulfillment by querying its logs with the "restockIds" filter. A restock that was created, then processed, then confirmed returns one log per step.

You can also filter by "types" (created, processing, confirmed) and "after" / "before", or fetch a single log with GET /v2/issuing-restock/log/:id.

**Request**

```python
import starkinfra

logs = starkinfra.issuingrestock.log.query(restock_ids=["6724771005489152"])

for log in logs:
    print(log)
```

**Response**

```python
Log(
    id=7834882116598271,
    type=created,
    restock=IssuingRestock(id=6724771005489152, status=created, ...),
    created=2022-01-01 00:00:00
)
Log(
    id=5189683645874176,
    type=processing,
    restock=IssuingRestock(id=6724771005489152, status=processing, ...),
    created=2022-03-01 00:00:00
)
Log(
    id=6284441486065664,
    type=confirmed,
    restock=IssuingRestock(id=6724771005489152, status=confirmed, ...),
    created=2022-06-01 00:00:00
)
```

## Issuing Embossing Request Overview

An Issuing Embossing Request is the order to physically produce and ship a physical Issuing Card. After you create a physical card (which starts as "pending"), you submit an embossing request to have it printed, packaged and shipped to the holder.

Prerequisites. You need an existing physical Issuing Card (its cardId), an Issuing Embossing Kit (the kitId — the plastic, packaging and design to print), and available stock for that kit. Once the card reaches the holder, it is activated separately by setting its status to "active" together with a PIN.

Batch. Send a "requests" list to order up to 100 at once. Each request needs the cardId and kitId, the name embossed on the card (displayName1), and the full shipping address plus shippingService (the carrier, e.g. "loggi") and a shippingTrackingNumber. Optional fields: embosserId, displayName2 / displayName3, shippingPhone and tags.

Tracking the shipment. shippingTrackingNumber is the code you use to follow the parcel with the carrier; the request's status and Logs tell you how production and dispatch are progressing.

### Embossing Request Statuses

Each Issuing Embossing Request has a status that reflects where its order is in the production and shipping flow:

| Status | Description |
| --- | --- |
| created | The request was registered and is queued for production. |
| processing | The card is being embossed and prepared for shipping. |
| success | The card was embossed and shipped. |
| failed | The request could not be fulfilled. |

### Embossing Request Logs

Every time an embossing request advances through production and dispatch, we create a Log entry, so you can follow an order without polling the request itself. The "request" field in each log is a representation of the Issuing Embossing Request as it was at that point (shown abbreviated with "...").

The log types are: created, sending, sent, success and failed. Query the logs with GET /v2/issuing-embossing-request/log (filter by requestIds, types, after / before), or fetch a single one with GET /v2/issuing-embossing-request/log/:id.

### Creating an Embossing Request

`POST /v2/issuing-embossing-request`

Order the physical production and shipping of one or more cards by sending a "requests" list. Each entry needs cardId, kitId, displayName1, the shipping address (shippingStreetLine1, shippingStreetLine2, shippingDistrict, shippingCity, shippingStateCode, shippingZipCode, shippingCountryCode), shippingService and shippingTrackingNumber.

New requests come back with status "created" — they are queued for production, not yet embossed.

**Request**

```python
import starkinfra

requests = starkinfra.issuingembossingrequest.create([
    starkinfra.IssuingEmbossingRequest(
        card_id="5715709195239424",
        kit_id="6724771005489152",
        display_name_1="TONY STARK",
        shipping_city="Sao Paulo",
        shipping_country_code="BRA",
        shipping_district="Bela Vista",
        shipping_service="loggi",
        shipping_state_code="SP",
        shipping_street_line_1="Av. Paulista, 1000",
        shipping_street_line_2="Apto 101",
        shipping_tracking_number="BR1234567890",
        shipping_zip_code="01311-000",
        tags=["department: tech"]
    )
])

for request in requests:
    print(request)
```

**Response**

```python
IssuingEmbossingRequest(
    id=7834882116598271,
    card_id=5715709195239424,
    kit_id=6724771005489152,
    display_name_1=TONY STARK,
    display_name_2=None,
    display_name_3=None,
    shipping_city=Sao Paulo,
    shipping_country_code=BRA,
    shipping_district=Bela Vista,
    shipping_phone=+5511999999999,
    shipping_service=loggi,
    shipping_state_code=SP,
    shipping_street_line_1=Av. Paulista, 1000,
    shipping_street_line_2=Apto 101,
    shipping_tracking_number=BR1234567890,
    shipping_zip_code=01311-000,
    embosser_id=5656565656565656,
    fee=0,
    status=created,
    tags=['department: tech'],
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Querying Embossing Requests

`GET /v2/issuing-embossing-request`

List your embossing requests in pages of up to 100. Filter by "status", "cardIds", "ids", "tags" and "after" / "before". Page through results with the returned "cursor".

**Request**

```python
import starkinfra

requests = starkinfra.issuingembossingrequest.query(
    limit=10,
    status="processing"
)

for request in requests:
    print(request)
```

**Response**

```python
IssuingEmbossingRequest(
    id=7834882116598271,
    card_id=5715709195239424,
    kit_id=6724771005489152,
    display_name_1=TONY STARK,
    display_name_2=None,
    display_name_3=None,
    shipping_city=Sao Paulo,
    shipping_country_code=BRA,
    shipping_district=Bela Vista,
    shipping_phone=+5511999999999,
    shipping_service=loggi,
    shipping_state_code=SP,
    shipping_street_line_1=Av. Paulista, 1000,
    shipping_street_line_2=Apto 101,
    shipping_tracking_number=BR1234567890,
    shipping_zip_code=01311-000,
    embosser_id=5656565656565656,
    fee=0,
    status=processing,
    tags=['department: tech'],
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Getting an Embossing Request

`GET /v2/issuing-embossing-request/:id`

Fetch a single embossing request by its id to check its current status and shipping details.

**Request**

```python
import starkinfra

request = starkinfra.issuingembossingrequest.get("7834882116598271")

print(request)
```

**Response**

```python
IssuingEmbossingRequest(
    id=7834882116598271,
    card_id=5715709195239424,
    kit_id=6724771005489152,
    display_name_1=TONY STARK,
    display_name_2=None,
    display_name_3=None,
    shipping_city=Sao Paulo,
    shipping_country_code=BRA,
    shipping_district=Bela Vista,
    shipping_phone=+5511999999999,
    shipping_service=loggi,
    shipping_state_code=SP,
    shipping_street_line_1=Av. Paulista, 1000,
    shipping_street_line_2=Apto 101,
    shipping_tracking_number=BR1234567890,
    shipping_zip_code=01311-000,
    embosser_id=5656565656565656,
    fee=0,
    status=success,
    tags=['department: tech'],
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Getting an Embossing Request's Logs

`GET /v2/issuing-embossing-request/log`

Follow an order's production and dispatch by querying its logs with the "requestIds" filter. You can also filter by "types" (created, sending, sent, success, failed) and "after" / "before", or fetch a single log with GET /v2/issuing-embossing-request/log/:id.

**Request**

```python
import starkinfra

logs = starkinfra.issuingembossingrequest.log.query(
    limit=10,
    request_ids=["7834882116598271"]
)

for log in logs:
    print(log)
```

**Response**

```python
Log(
    id=8945993227707382,
    type=created,
    request=IssuingEmbossingRequest(id=7834882116598271, status=created, ...),
    created=2022-01-01 00:00:00
)
Log(
    id=5547499534213120,
    type=sent,
    request=IssuingEmbossingRequest(id=7834882116598271, status=processing, ...),
    created=2022-02-01 00:00:00
)
```

## Issuing Token Overview

An Issuing Token is an Issuing Card provisioned into a digital wallet — Apple Pay or Google Pay. The token is a wallet-specific stand-in for the card: purchases run through it without ever exposing the real card number.

You do not create a token here. Tokens are created through the Issuing Token Request provisioning flow, when a cardholder adds the card to a wallet. This section covers what you do afterwards: listing tokens, inspecting one, blocking or unblocking it, canceling it, and auditing its history.

Reading the object. walletId / walletName identify the wallet the card was added to (e.g. "apple" / "Apple Pay"). cardId points back to the Issuing Card that was tokenized, and merchantId is set only when the token is merchant-specific. externalId is the wallet's own identifier for the token. walletDeviceScore and walletAccountScore are risk scores the wallet reports at provisioning time — they are null when the wallet did not send them.

### Token Statuses

Each Issuing Token has a status that reflects where it is in its life cycle:

| Status | Description |
| --- | --- |
| pending | The wallet requested provisioning and the token is awaiting approval. It cannot be used for purchases yet. |
| active | The token was provisioned and can be used for purchases. |
| frozen | The wallet has temporarily suspended the token (for example, the device was locked or the card was suspended in the wallet). This state is driven by the wallet, not by this API. |
| blocked | The token is blocked and declines purchases. Unblock it by setting its status back to "active". |
| denied | The provisioning request was rejected, so the token was never activated. |
| canceled | The token was permanently canceled and removed from the wallet. This is irreversible. |

### Token Logs

Every change to an Issuing Token — including the wallet-driven provisioning steps — creates a Log entry, so you can audit a token's full history. The "token" field in each log is a representation of the Issuing Token as it was at that point (shown abbreviated with "...").

Each log carries a "type". The possible types are: created, denied, updated, unblocking, unblocked, blocking, blocked, frozen, unfrozen, canceling, canceled and failed. The transient "-ing" types (unblocking, blocking, canceling) mark a state change that was requested with the wallet and is still being confirmed; the matching past-tense type is emitted once it settles. "failed" marks a provisioning or status change the wallet rejected.

Query the token logs with GET /v2/issuing-token/log (filter by tokenIds, types, tokenTags, after / before), or fetch a single one with GET /v2/issuing-token/log/:id.

### Listing Tokens

`GET /v2/issuing-token`

List the tokens that have been provisioned from your cards, in chunks of at most 100. Filter by "status", "cardIds" (to see every wallet a given card was added to), "externalIds", "tags" or "after" / "before".

Use the returned "cursor" to page through results — pass it back as the "cursor" parameter to get the next batch. A null cursor means there are no more pages.

**Request**

```python
import starkinfra

tokens = starkinfra.issuingtoken.query(
    limit=10,
    status="active"
)

for token in tokens:
    print(token)
```

**Response**

```python
IssuingToken(
    id=5715709195239424,
    card_id=6724771005489152,
    wallet_id=apple,
    wallet_name=Apple Pay,
    merchant_id=5656565656565656,
    external_id=DNITHE301234567890123456789012345678,
    wallet_device_score=None,
    wallet_account_score=None,
    status=active,
    tags=['department: tech'],
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Getting a Token

`GET /v2/issuing-token/:id`

Fetch a single token by its id to inspect which card and wallet it belongs to and its current status.

**Request**

```python
import starkinfra

token = starkinfra.issuingtoken.get("5715709195239424")

print(token)
```

**Response**

```python
IssuingToken(
    id=5715709195239424,
    card_id=6724771005489152,
    wallet_id=apple,
    wallet_name=Apple Pay,
    merchant_id=5656565656565656,
    external_id=DNITHE301234567890123456789012345678,
    wallet_device_score=None,
    wallet_account_score=None,
    status=active,
    tags=['department: tech'],
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Blocking a Token

`PATCH /v2/issuing-token/:id`

Block a token by setting its status to "blocked". A blocked token declines purchases through that wallet until you unblock it; the underlying Issuing Card and any other wallets keep working. You can also send a new "tags" array on the same request.

**Request**

```python
import starkinfra

token = starkinfra.issuingtoken.update(
    id="5715709195239424",
    status="blocked"
)

print(token)
```

**Response**

```python
IssuingToken(
    id=5715709195239424,
    card_id=6724771005489152,
    wallet_id=apple,
    wallet_name=Apple Pay,
    merchant_id=5656565656565656,
    external_id=DNITHE301234567890123456789012345678,
    wallet_device_score=None,
    wallet_account_score=None,
    status=blocked,
    tags=['department: tech'],
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Unblocking a Token

`PATCH /v2/issuing-token/:id`

Unblock a previously blocked token by setting its status back to "active".

**Request**

```python
import starkinfra

token = starkinfra.issuingtoken.update(
    id="5715709195239424",
    status="active"
)

print(token)
```

**Response**

```python
IssuingToken(
    id=5715709195239424,
    card_id=6724771005489152,
    wallet_id=apple,
    wallet_name=Apple Pay,
    merchant_id=5656565656565656,
    external_id=DNITHE301234567890123456789012345678,
    wallet_device_score=None,
    wallet_account_score=None,
    status=active,
    tags=['department: tech'],
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Canceling a Token

`DELETE /v2/issuing-token/:id`

Permanently cancel a token by its id. Its status becomes "canceled" and it is removed from the wallet. This action is irreversible — a token that is already canceled cannot be canceled again.

**Request**

```python
import starkinfra

token = starkinfra.issuingtoken.cancel("5715709195239424")

print(token)
```

**Response**

```python
IssuingToken(
    id=5715709195239424,
    card_id=6724771005489152,
    wallet_id=apple,
    wallet_name=Apple Pay,
    merchant_id=5656565656565656,
    external_id=DNITHE301234567890123456789012345678,
    wallet_device_score=None,
    wallet_account_score=None,
    status=canceled,
    tags=['department: tech'],
    created=2022-01-01 00:00:00,
    updated=2022-06-01 00:00:00
)
```

### Getting a Token's Logs

`GET /v2/issuing-token/log`

Audit a token's history by querying its logs with the "tokenIds" filter. Each step — provisioning, blocking, canceling and so on — produces one log. You can also filter by "types", "tokenTags" and "after" / "before", or fetch a single log with GET /v2/issuing-token/log/:id.

**Request**

```python
import starkinfra

logs = starkinfra.issuingtoken.log.query(
    limit=10,
    token_ids=["5715709195239424"]
)

for log in logs:
    print(log)
```

**Response**

```python
Log(
    id=6724771005489152,
    type=created,
    errors=[],
    token=IssuingToken(id=5715709195239424, status=active, ...),
    created=2022-01-01 00:00:00
)
Log(
    id=5547499534213120,
    type=blocked,
    errors=[],
    token=IssuingToken(id=5715709195239424, status=blocked, ...),
    created=2022-02-01 00:00:00
)
```

## Issuing Token Request Overview

An Issuing Token Request starts the provisioning of an Issuing Card into a digital wallet (Apple Pay, Google Pay or a merchant wallet). It is the first step of tokenization: you call it, get back a signed payload, and hand that payload to the wallet's SDK on the device to finish adding the card.

The card is identified by its id (cardId) — the id Stark Infra returned when you created the Issuing Card.

walletId. This is NOT a token or a JWT — it is the wallet the card is being added to. Send one of "apple", "google" or "merchant".

methodCode. The provisioning method: "app" (in-app provisioning, where your app drives the flow through the wallet SDK) or "manual" (the cardholder types the card details into the wallet themselves).

The returned payload. The response gives you a "content" and a "signature". These are the inputs the wallet SDK expects to complete provisioning on the device. Stark Infra does not store a token request as a queryable resource — there is no status, no lifecycle and no log: once you have the content and signature, the rest of the flow happens between the device and the wallet, producing an Issuing Token.

### Starting a tokenization

`POST /v2/issuing-token-request`

Send the card's id (cardId), the target walletId ("apple", "google" or "merchant") and the methodCode ("app" or "manual"). You may also attach a "metadata" object to carry your own data through the flow.

The response echoes back what you sent plus the "content" and "signature" you forward to the wallet SDK on the device. The result is wrapped in a "request" object.

**Request**

```python
import starkinfra

token_request = starkinfra.issuingtokenrequest.create(
    starkinfra.IssuingTokenRequest(
        card_id="5715709195239424",
        wallet_id="apple",
        method_code="app"
    )
)

print(token_request)
```

**Response**

```python
IssuingTokenRequest(
    card_id=5715709195239424,
    wallet_id=apple,
    method_code=app,
    content=eyJhcHBsaWNhdGlvbklkIjoiY29tLmFwcGxl...,
    signature=MEUCIQDp3GTGqsqDeKLN9aLhxAZ...,
    metadata={}
)
```

## Issuing Token Design Overview

An Issuing Token Design controls how a card looks inside a digital wallet — it is the artwork displayed for an Issuing Token once an Issuing Card has been tokenized into Apple Pay or Google Pay.

You provide the artwork; Stark Infra provisions it. The card design itself comes from you, the sub-issuer: send your artwork to help@starkinfra.com and Stark Infra provisions it with your card networks and makes the resulting Token Design available to your workspace.

Read-only through the API. Token designs are not created, changed or deleted through the API. Once provisioned, the API only lets you list the designs available to your workspace, fetch one by its id, and download its artwork as a PDF.

What you get back. Each design is a thin record: an id you reference when requesting a token, a human-readable name (e.g. "Apple Pay Card Art"), and created / updated datetimes. The visual asset itself is not part of the JSON — fetch it through the PDF endpoint.

### Listing Token Designs

`GET /v2/issuing-token-design`

List the token designs available to your workspace. Filter by "ids" to fetch specific designs, and page through results with "limit" (max 100) and "cursor".

Use the "id" of the design you want when requesting an Issuing Token, so the tokenized card shows the right artwork in the wallet.

**Request**

```python
import starkinfra

designs = starkinfra.issuingtokendesign.query(limit=10)

for design in designs:
    print(design)
```

**Response**

```python
IssuingTokenDesign(
    id=5715709195239424,
    name=Apple Pay Card Art,
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Getting a Token Design

`GET /v2/issuing-token-design/:id`

Fetch a single token design by its id. A design only resolves if it belongs to your sub-issuer — fetching another sub-issuer's design is rejected.

**Request**

```python
import starkinfra

design = starkinfra.issuingtokendesign.get("5715709195239424")

print(design)
```

**Response**

```python
IssuingTokenDesign(
    id=5715709195239424,
    name=Apple Pay Card Art,
    created=2022-01-01 00:00:00,
    updated=2022-01-01 00:00:00
)
```

### Downloading the Token Design PDF

`GET /v2/issuing-token-design/:id/pdf`

Download the design's artwork as a PDF. This endpoint returns the raw binary file (no JSON wrapper), so save it straight to disk — with curl, use "--output" to write it to a file.

**Request**

```python
import starkinfra

pdf = starkinfra.issuingtokendesign.pdf("5715709195239424")

with open("token-design.pdf", "wb") as f:
    f.write(pdf)
```

**Response**

```python
b'%PDF-1.4 ...' # Binary PDF content
```

## Handling Purchase Events

In this section we're going deeper with the Issuing Purchase Events, that will allow you to handle the scenarios mentioned earlier.

First of all, you need to create a Webhook endpoint with issuing-purchase subscription.

### Creating a Webhook

**Request**

```python
import starkinfra

webhook = starkinfra.webhook.create(
    url="https://winterfell.westeros.gov/events-from-stark-infra",
    subscriptions=[
        "issuing-purchase",
    ]
)

print(webhook)
```

**Response**

```python
Webhook(
    id=6081803731337216,
    subscriptions=["issuing-purchase"],
    url=https://winterfell.westeros.gov/events-from-stark-infra
)
```

### Receiving Events

Once created, you'll start to receive the Events at the informed URL. The Event will look like this:

**Request**

```python
{
    "event": {
        "created": "2022-09-29T19:34:50.798935+00:00",
        "id": "4673184305512448",
        "log": {
            "created": "2022-09-29T19:34:50.302804+00:00",
            "errors": [],
            "id": "5946009943277568",
            "issuingTransactionId": "6227484919988224",
            "purchase": {
                "acquirerId": "557045",
                "amount": 10000,
                "cardEnding": "1787",
                "cardId": "5975559855144960",
                "created": "2022-09-29T19:34:50.151607+00:00",
                "description": "",
                "endToEndId": "60ae1ddd-ebb0-432f-88c7-ac8014ee3c5f",
                "holderName": "TONY STARK",
                "id": "5101585013145600",
                "issuerAmount": 10000,
                "issuerCurrencyCode": "BRL",
                "issuerCurrencySymbol": "R$",
                "issuingTransactionIds": [
                    "6227484919988224"
                ],
                "merchantAmount": 10000,
                "merchantCategoryCode": "miscellaneousBusinessServices",
                "merchantCategoryType": "services",
                "merchantCountryCode": "BRA",
                "merchantCurrencyCode": "BRL",
                "merchantCurrencySymbol": "R$",
                "merchantFee": 0,
                "merchantId": "731138412121896",
                "merchantName": "GOOGLE CLOUD PLATFORM",
                "methodCode": "manual",
                "purpose": "purchase",
                "score": 0.9992851125568876,
                "status": "approved",
                "tags": [
                    "tony",
                    "stark"
                ],
                "tax": 0,
                "updated": "2022-09-29T19:34:50.302847+00:00",
                "walletId": "",
                "zipCode": "902101234"
            },
            "type": "approved"
        },
        "subscription": "issuing-purchase",
        "workspaceId": "5506710584164352"
    }
}
```

### Parsing Events

If your endpoint URL does not return a 200 status, the webhook service will try again at most three times. The interval between each attempt is 5 min, 30 min and finally 120 min. In case the event cannot be delivered after those three attempts, we will stop trying to deliver the message.

Listening to Issuing Purchase Events After answering a Issuing Purchase authorization request, you can use asynchronous Webhooks to monitor status changes of the entity. Issuing Purchases will follow the following life cycle:

Every time we change an Issuing Purchase, we create a Log. Logs are pretty useful for understanding the life cycle of each Issuing Purchase. Whenever a new Log is created, we will fire a Webhook to your registered URL. Check out this diagram to understand the possible Issuing Purchase Logs:

TYPES AND STATUS DEFINITIONSApprovedAn Issuing Purchase authorization request was approved by the sub-issuer or by the network/issuer default authorization protocol. The amount of money is reserved for the future settlement.ConfirmedA purchase was settled by the network. At this point, the amount of money is transfered through the network.ReversedSome amount of the purchase was reversed, it can be partially or fully reversed.VoidedOnce a purchase is fully reversed, its status changes to voided.DeniedAn Issuing Purchase authorization request was denied by the sub-issuer or by the network/issuer default authorization protocol.CanceledAn approved purchase was canceled by the acquirer. The reserved amount of money goes back to your Issuing Balance.UpdatedA new updated Log is generated when you set a description or tags for the purchase.

**Request**

```python
import starkinfra

request = listen()  # this is the method you made to get the events posted to your webhook endpoint

event = starkinfra.event.parse(
    content=request.data.decode("utf-8"),
    signature=request.headers["Digital-Signature"],
)

if "issuing-purchase" in event.subscription:
    print(event.log.purchase)
```

### Success Webhook Example

4.1. Success Webhook Example

**Request**

```python
{
    "event": {
        "created": "2022-09-29T19:34:50.798935+00:00",
        "id": "4673184305512448",
        "log": {
            "created": "2022-09-29T19:34:50.302804+00:00",
            "errors": [],
            "id": "5946009943277568",
            "issuingTransactionId": "6227484919988224",
            "purchase": {
                "acquirerId": "557045",
                "amount": 10000,
                "cardEnding": "1787",
                "cardId": "5975559855144960",
                "created": "2022-09-29T19:34:50.151607+00:00",
                "description": "",
                "endToEndId": "60ae1ddd-ebb0-432f-88c7-ac8014ee3c5f",
                "holderName": "TONY STARK",
                "id": "5101585013145600",
                "issuerAmount": 10000,
                "issuerCurrencyCode": "BRL",
                "issuerCurrencySymbol": "R$",
                "issuingTransactionIds": [
                    "6227484919988224"
                ],
                "merchantAmount": 10000,
                "merchantCategoryCode": "miscellaneousBusinessServices",
                "merchantCategoryType": "services",
                "merchantCountryCode": "BRA",
                "merchantCurrencyCode": "BRL",
                "merchantCurrencySymbol": "R$",
                "merchantFee": 0,
                "merchantId": "731138412121896",
                "merchantName": "GOOGLE CLOUD PLATFORM",
                "methodCode": "manual",
                "purpose": "purchase",
                "score": 0.9992851125568876,
                "status": "approved",
                "tags": [
                    "tony",
                    "stark"
                ],
                "tax": 0,
                "updated": "2022-09-29T19:34:50.302847+00:00",
                "walletId": "",
                "zipCode": "902101234"
            },
            "type": "approved"
        },
        "subscription": "issuing-purchase",
        "workspaceId": "5506710588664374"
    }
}
```

### Failed Webhook Example

4.2. Failed Webhook Example

**Request**

```python
{
    "event": {
        "created": "2022-09-29T19:43:22.799782+00:00",
        "id": "6012690115854336",
        "log": {
            "created": "2022-09-29T19:43:22.244999+00:00",
            "errors": [
                {
                    "code": "concurrency",
                    "message": "More than one authorization was attempted at the same time on this card."
                }
            ],
            "id": "5146374676938752",
            "issuingTransactionId": "",
            "purchase": {
                "acquirerId": "230851",
                "amount": 10000,
                "cardEnding": "1787",
                "cardId": "5975559855144960",
                "created": "2022-09-29T19:43:22.175174+00:00",
                "description": "",
                "endToEndId": "920b2027-9bcb-476c-a23d-33d15a2a1470",
                "holderName": "TONY STARK",
                "id": "5709324630360064",
                "issuerAmount": 10000,
                "issuerCurrencyCode": "BRL",
                "issuerCurrencySymbol": "R$",
                "issuingTransactionIds": [],
                "merchantAmount": 10000,
                "merchantCategoryCode": "miscellaneousBusinessServices",
                "merchantCategoryType": "services",
                "merchantCountryCode": "BRA",
                "merchantCurrencyCode": "BRL",
                "merchantCurrencySymbol": "R$",
                "merchantFee": 0,
                "merchantId": "360698645394776",
                "merchantName": "GOOGLE CLOUD PLATFORM",
                "methodCode": "manual",
                "purpose": "purchase",
                "score": 0.9996876949639208,
                "status": "denied",
                "tags": [
                    "tony",
                    "stark"
                ],
                "tax": 0,
                "updated": "2022-09-29T19:43:22.245043+00:00",
                "walletId": "",
                "zipCode": "902101234"
            },
            "type": "denied"
        },
        "subscription": "issuing-purchase",
        "workspaceId": "5506710588664374"
    }
}
```

---

## Other Languages

- [Python](https://docs.starkinfra.com/get-started/card-issuing-python.md)
- [Node.js](https://docs.starkinfra.com/get-started/card-issuing-node.md)
- [PHP](https://docs.starkinfra.com/get-started/card-issuing-php.md)
- [Java](https://docs.starkinfra.com/get-started/card-issuing-java.md)
- [Ruby](https://docs.starkinfra.com/get-started/card-issuing-ruby.md)
- [Elixir](https://docs.starkinfra.com/get-started/card-issuing-elixir.md)
- [.NET](https://docs.starkinfra.com/get-started/card-issuing-dotnet.md)
- [Go](https://docs.starkinfra.com/get-started/card-issuing-go.md)
- [Clojure](https://docs.starkinfra.com/get-started/card-issuing-clojure.md)
- [cURL](https://docs.starkinfra.com/get-started/card-issuing-curl.md)
