Card

Cards: virtual cards with limits, for agents that buy things.

card = noclick.Card.create("Office supplies", limit_usd=1500, interval="month",
                           merchants=["office_supplies"], agent=buyer, require_approval_above_usd=200)
details = card.purchase(124.50, "Staples", reason="Toner").wait()   # approved first when above $200
for charge in card.transactions(limit=20):
    print(charge.merchant, charge.amount_usd, charge.status)
card.freeze()

Cards are your company's commercial cards, for US businesses, in dollars. They spend your company's own card balance, held by Stripe Payments Company (Card.funding()), which you fund by bank transfer: what a card pays isn't charged to your NoClick account, which pays only $0.13 per card number issued. Cards need card issuing, which your company applies for once in the console (Card.program()); until it's active these calls raise noclick.NotAvailable. A card number is shown only on a page where the card issuer's own element displays it (reveal()): it never passes through NoClick, your code or a model.

class Card

A virtual card with a spending limit. Card.from_name("Office supplies", create_if_missing=True, limit_usd=1500) is lazy; card.purchase(...) approves and pays one purchase, card.reveal() shows its number to a person.

A card is always your company's, for its business, including buying for one of your users: a card issued for a user (user=) is one only that user's agents can pay with, never a card the user owns or funds. It may be held by one agent, which pays with it at checkouts in its browser.

Attributes
idstr
namestr
last4str | None
brandstr | None

The card network, like visa.

exp_monthint | None
exp_yearint | None
statusstr

active, frozen or closed.

status_reasonstr | None

Why it's frozen when nobody froze it: negative_balance while your card balance is below zero (it pays again once funded). None otherwise.

limit_usdfloat | None

The most it spends per interval (None: no limit).

intervalstr | None

What limit_usd covers: transaction, day, month or all_time.

spent_usdfloat | None

Spent in the current interval (this calendar month's spend for a per-transaction limit).

resets_atAny

When spent_usd starts over (None for all_time).

merchantslist[str] | None

The merchant categories it may pay (None: any).

require_approval_above_usdfloat | None

Charges above this wait for a person (None: never).

agent_idstr | None

The agent that pays with it (None if none).

single_usebool | None

Whether every checkout gets a fresh number that takes one charge (last4 is the latest one's).

user_idstr | None
created_atAny

createclassmethodasync

noclick.Card.create(
    name, *, limit_usd, interval='month', merchants=None, agent=None,
    require_approval_above_usd=None, single_use=False, user=..., client=None
) -> Card

Issue a virtual card called name (unique among your open cards in its user; a duplicate raises noclick.Conflict with existing_id).

limit_usd caps what it spends per interval ("transaction", "day", "month" or "all_time"). merchants names the merchant categories it may pay ("office_supplies", "software", "travel", ...; any when omitted; never cash, money transfers, gambling or securities). agent (an noclick.Agent or id) is the agent that pays with it. A purchase above require_approval_above_usd is approved by a person before anything is paid (purchase); a charge above it that nobody announced is declined and the merchant's retry passes once they approve. single_use=True gives every checkout (every purchase, every reveal()) a fresh number that takes one charge and then closes, so a merchant that keeps a number can never charge it again; together they spend within limit_usd per interval (UTC days and months).

Parameters
namestrrequired
limit_usdfloatrequired
intervalstrdefault 'month'
merchantsSequence[str] | Nonedefault None
agentAnydefault None
require_approval_above_usdfloat | Nonedefault None
single_usebooldefault False
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
Card

The new card, active, with its last4 and expiry (a single-use card has none until its first checkout). Its number is only ever shown on reveal()'s page. Each number costs $0.13.

Usage

A monthly card for one agent, where anything over $200 needs a yes:

buyer = noclick.Agent.from_name("concierge", user="acme")
card = noclick.Card.create(
    "Office supplies", limit_usd=1500, interval="month",
    merchants=["office_supplies"], agent=buyer, require_approval_above_usd=200, user="acme",
)
print(card.last4, card.limit_usd, card.interval)

A card for your own code, capped per charge instead of per month:

card = noclick.Card.create("Software", limit_usd=200, interval="transaction", merchants=["software"])

A fresh number for every purchase, for merchants you'd rather not keep one:

buyer = noclick.Agent.from_name("buyer")
card = noclick.Card.create("Marketplace", limit_usd=500, interval="month", agent=buyer, single_use=True)

from_nameclassmethod

noclick.Card.from_name(
    name, *, create_if_missing=False, limit_usd=None, interval='month', merchants=None,
    agent=..., require_approval_above_usd=..., single_use=None, user=..., client=None
) -> Card

A lazy reference to the open card called name in the current (or given) user. With create_if_missing=True your code is the definition: it's issued on first use with create's fields, and an existing card whose limit, interval, merchants, approval threshold, agent or single_use differ is updated to match. A field you leave out is never changed, and None clears agent or require_approval_above_usd, as in update().

Parameters
namestrrequired
create_if_missingbooldefault False
limit_usdfloat | Nonedefault None
intervalstrdefault 'month'
merchantsSequence[str] | Nonedefault None
agentAnyomit to leave it unchanged
require_approval_above_usdAnyomit to leave it unchanged
single_usebool | Nonedefault None
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
Card

A card that loads (or is issued, or brought in step with your definition) the first time it's used. Nothing is sent yet.

Usage

Refer to a card by name:

card = noclick.Card.from_name("Office supplies", user="acme")
print(card.spent_usd, card.limit_usd)

Or make your code the definition. limit_usd is required then:

card = noclick.Card.from_name(
    "Travel", create_if_missing=True, limit_usd=3000, interval="month",
    merchants=["travel"], agent=noclick.Agent.from_name("assistant", user="user_42"), user="user_42",
)
print(card.last4)

from_idclassmethod

noclick.Card.from_id(card_id, *, client=None, user=...) -> Card

A lazy reference to a card by id.

Parameters
card_idstrrequired
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
Returns
Card

A card that loads the first time you read a field. Nothing is sent yet.

Usage
card = noclick.Card.from_id("c4a7e2b9-3d1f-4a8e-b6c5-9f2d0e7a1b36")
card.freeze()

getclassmethodasync

noclick.Card.get(card_id, *, user=..., client=None) -> Card

Load a card by id now; an unknown id raises noclick.NotFound.

Parameters
card_idstrrequired
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
Card

The card, loaded, with this interval's spent_usd.

Usage
card = noclick.Card.get("c4a7e2b9-3d1f-4a8e-b6c5-9f2d0e7a1b36")
print(card.name, card.status, card.spent_usd, card.limit_usd)

listclassmethodasync

noclick.Card.list(
    *, status=None, agent=None, limit=None, user_filter=None, user=..., client=None
) -> list[Card]

The current user's cards (the account's own outside as_user, or those user_filter covers), optionally one status or one agent's.

Parameters
statusstr | Nonedefault None
agentAnydefault None
limitint | None

Return at most this many.

(default None)
user_filterAny

Which users a read covers: noclick.ALL_USERS, noclick.NO_USER, a User, or your id for one. It filters without acting as that user.

(default None)
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
list[Card]

Every matching card, newest first, fetched page by page. Closed cards are listed only with status="closed".

Usage
for card in noclick.Card.list(user_filter=noclick.ALL_USERS):
    print(card.name, card.last4, card.status, card.spent_usd)

frozen = noclick.Card.list(status="frozen", user="user_42")

list_pageclassmethodasync

noclick.Card.list_page(
    *, cursor=None, limit=None, status=None, agent=None, user_filter=None, user=...,
    client=None
) -> Page

One page of cards; pass next_cursor back as cursor.

Parameters
cursorstr | None

The next_cursor of the page before; omit it for the first page.

(default None)
limitint | None

Return at most this many.

(default None)
statusstr | Nonedefault None
agentAnydefault None
user_filterAny

Which users a read covers: noclick.ALL_USERS, noclick.NO_USER, a User, or your id for one. It filters without acting as that user.

(default None)
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
Page

A page whose data holds the cards and whose next_cursor is None on the last page.

Usage
page = noclick.Card.list_page(limit=50, user_filter=noclick.ALL_USERS)
while True:
    for card in page.data:
        print(card.name, card.status)
    if page.next_cursor is None:
        break
    page = noclick.Card.list_page(limit=50, user_filter=noclick.ALL_USERS, cursor=page.next_cursor)

fundingclassmethodasync

noclick.Card.funding(*, client=None) -> CardFunding

Your company's card balance, which every card (the account's and every user's) spends from.

Parameters
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
CardFunding

The balance_usd, what pending charges hold (pending_usd), what cards can still spend (available_usd), and the bank_transfer details that fund it.

Usage
funding = noclick.Card.funding()
print(funding.balance_usd, funding.pending_usd, funding.available_usd)
if funding.available_usd is not None and funding.available_usd < 100:
    bank = funding.bank_transfer
    print("Wire funds to", bank.bank_name, bank.routing_number, bank.account_number)

programclassmethodasync

noclick.Card.program(*, client=None) -> CardProgram

Where your company's card issuing stands. It's applied for once, in the console.

Parameters
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
CardProgram

The program's status (not_started, pending_verification, active, rejected or restricted), what it still needs (requirements_due) and where to apply (onboarding_url).

Usage
program = noclick.Card.program()
if program.status != "active":
    print("Apply for cards at", program.onboarding_url, program.requirements_due)

merchant_categoriesclassmethodasync

noclick.Card.merchant_categories(*, client=None) -> list[CardMerchantCategory]

The names merchants takes, each with the merchant category codes it covers (a 4-digit code works too).

Parameters
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
list[CardMerchantCategory]

Every category, like office_supplies, software and travel.

Usage
for category in noclick.Card.merchant_categories():
    print(category.name, category.description, category.mccs)

transactionsasync

card.transactions(*, status=None, limit=20) -> list[CardTransaction]

Its charges and refunds, newest first: status narrows to approved, settled, reversed, declined or pending_approval.

Parameters
statusstr | Nonedefault None
limitint | None

Return at most this many.

(default 20)
Returns
list[CardTransaction]

The newest limit (20 by default; None for every one), each with its amount, merchant, status, for a decline why, and any dispute.

Usage

See what the card paid and what it refused:

card = noclick.Card.from_name("Office supplies", user="acme")
for charge in card.transactions(limit=20):
    print(charge.at, charge.merchant, charge.amount_usd, charge.status, charge.decline_reason)

Wait on a charge that's held for a person:

card = noclick.Card.from_name("Office supplies", user="acme")
for charge in card.transactions(status="pending_approval"):
    approved = noclick.Operation.from_id(charge.approval_id).wait()
    print(charge.merchant, "approved" if approved else "declined")

revealasync

card.reveal() -> CardDetails

A page that shows the card's number to a person: the card issuer's own element displays it there, for 10 minutes, so the number never passes through NoClick or your code. On a single-use card it's a fresh number, which closes unused after an hour. Needs a key with the cards:reveal scope; every reveal is recorded with the key that made it.

Returns
CardDetails

The page's url, when it stops showing the number, the number's last4 and expiry, and the cardholder name and billing address a checkout asks for.

Usage
card = noclick.Card.from_name("Software")
details = card.reveal()
print(details)   # CardDetails(expires_at=..., last4='4242', ...): the url stays out of logs
send_to_finance(details.url)   # a person opens it and pays the invoice

purchaseasync

card.purchase(amount_usd, merchant, *, reason=None) -> Operation

Approve one purchase before paying it: a number for exactly this purchase, which takes one charge of at most amount_usd (tax and shipping included) and then closes. At or under require_approval_above_usd it's ready at once; above it, the card's user (or your account's owner) is asked first, with merchant and reason shown. Needs the cards:reveal scope.

Parameters
amount_usdfloatrequired
merchantstrrequired
reasonstr | Nonedefault None
Returns
Operation

A card.purchase operation; wait() returns the CardDetails to pay with once approved, and raises noclick.PolicyDenied (declined) or noclick.Expired (nobody answered in 30 minutes).

Usage
card = noclick.Card.from_name("Office supplies", user="acme")
details = card.purchase(124.50, "Staples", reason="Toner for the office").wait()
send_to_buyer(details.url)

disputeasync

card.dispute(
    transaction, *, reason, explanation, amount_usd=None, product=None,
    duplicate_of=None
) -> CardDispute

Dispute a settled charge with the card network: fraudulent (nobody here made it), canceled, duplicate (name the charge it copies in duplicate_of), not_received, not_as_described (say whether it was merchandise or a service in product) or incorrect_amount (with the amount_usd in dispute). A wrong purchase your agent made is a merchant dispute, not fraud.

Parameters
transactionAnyrequired
reasonstrrequired
explanationstrrequired
amount_usdfloat | Nonedefault None
productstr | Nonedefault None
duplicate_ofAnydefault None
Returns
CardDispute

The dispute, submitted; card.dispute.updated events follow it to won or lost.

Usage
card = noclick.Card.from_name("Office supplies", user="acme")
[charge] = card.transactions(limit=1)
dispute = card.dispute(charge, reason="not_received", explanation="Ordered Sep 2, never delivered.")
print(dispute.status)   # submitted

updateasync

card.update(
    *, name=..., limit_usd=..., interval=..., merchants=..., agent=...,
    require_approval_above_usd=..., single_use=...
) -> Card

Change its limits, merchants, approval threshold, agent, name or single_use (from its next checkout). None clears a field: merchants=None allows any merchant, require_approval_above_usd=None never asks, agent=None takes it from its agent.

Parameters
nameAnyomit to leave it unchanged
limit_usdAnyomit to leave it unchanged
intervalAnyomit to leave it unchanged
merchantsAnyomit to leave it unchanged
agentAnyomit to leave it unchanged
require_approval_above_usdAnyomit to leave it unchanged
single_useAnyomit to leave it unchanged
Returns
Card

This card, updated. A field you leave out is unchanged, and changes apply from the next charge.

Usage
card = noclick.Card.from_name("Office supplies", user="acme")
card.update(limit_usd=2000, require_approval_above_usd=300)
card.update(merchants=None)   # any merchant from now on

freezeasync

card.freeze() -> Card

Decline every charge until unfreeze(); the card keeps its number and limits.

Returns
Card

This card, frozen.

Usage
card = noclick.Card.from_name("Travel", user="user_42")
card.freeze()
print(card.status)   # frozen

unfreezeasync

card.unfreeze() -> Card

Let a frozen card pay again. While your card balance is below zero every card stays frozen (status_reason negative_balance): this raises noclick.Conflict until it's funded.

Returns
Card

This card, active.

Usage
card = noclick.Card.from_name("Travel", user="user_42")
card.unfreeze()
print(card.status)   # active

closeasync

card.close() -> Card

Close the card for good: it declines every charge from now on and its number is never reissued. Its transactions stay readable.

Returns
Card

This card, closed.

Usage
card = noclick.Card.from_name("Software")
card.close()
print(card.status)   # closed

CardTransactioninvite-only beta

class CardTransaction

One charge or refund on a card. A charge is approved (its amount held until the merchant settles it), settled (settled_amount_usd, which can differ for tips and shipping), reversed (released unsettled), declined or pending_approval (held for a person). A refund is settled, its amount_usd below zero. A decline says why in decline_reason: over_limit, over_budget (an app's card, past what's left of the app's budget), merchant_not_allowed, frozen, insufficient_funds, amount_mismatch (more than a purchase's number allows), not_approved, approval_expired, awaiting_approval, card_closed, card_expired, invalid_cvc, invalid_expiry, 3ds_failed or issuer_declined.

Attributes
idstr | None
card_idstr | None
kindstr | None
amount_usdfloat | None
settled_amount_usdfloat | None
merchantstr | None
merchant_categorystr | None

The Card.merchant_categories() name its merchant_mcc is in (None: none of them).

merchant_mccstr | None
statusstr | None
decline_reasonstr | None
approval_idstr | None

The held charge's approval, an operation: Operation.from_id(approval_id).wait() is True once approved.

purchase_idstr | None

The card.purchase operation whose number paid it.

last4str | None

The number it was on (a single-use card has one per checkout).

disputeCardDispute | None
atAny
rawdict[str, Any]

The response JSON exactly as the API sent it.

CardDetailsinvite-only beta

class CardDetails

A card number to pay with, shown only at url: a page where the card issuer's own element displays the number, CVC and expiry to whoever opens it, until expires_at. The number never passes through NoClick, your code or a model, so send url only to the person who pays. Its repr leaves url out.

Attributes
urlstr
expires_atAny
last4str | None
brandstr | None
exp_monthint | None
exp_yearint | None
single_usebool | None

A number that takes one charge (every number of a single-use card, every purchase's).

amount_usdfloat | None

A purchase's exact amount: the most its one charge may be.

closes_atAny

When a single-use number nobody paid with closes.

cardholderstr | None

The name and billing address a checkout asks for: your company's.

billing_addressdict[str, Any] | None
rawdict[str, Any]

The response JSON exactly as the API sent it.

CardFundinginvite-only beta

class CardFunding

Your company's card balance, held by Stripe Payments Company: every card spends from it. pending_usd is held by charges that haven't settled. The balance can drop below zero when a merchant takes a charge without asking (allowed even after a number closes); NoClick then freezes every card until it's funded again (frozen_since, and the card.balance.negative event).

Attributes
balance_usdfloat | None
pending_usdfloat | None
available_usdfloat | None

What cards can still spend: the balance less what's pending.

top_up_urlstr | None

The console page with these bank details.

bank_transferCardBankTransfer | None
frozen_sinceAny

Since when your cards have been frozen because the balance is below zero (None: they aren't).

rawdict[str, Any]

The response JSON exactly as the API sent it.

CardBankTransferinvite-only beta

class CardBankTransfer

Where your company sends money to fund its card balance: a wire lands the same day, an ACH transfer within a few business days.

Attributes
bank_namestr | None
routing_numberstr | None
account_numberstr | None
networkslist[str]

ach and wire.

CardDisputeinvite-only beta

class CardDispute

A settled charge disputed with the card network. status is submitted, then won (the money comes back to your card balance), lost or expired.

Attributes
idstr | None
transaction_idstr | None
reasonstr | None

fraudulent, canceled, duplicate, not_received, not_as_described or incorrect_amount.

statusstr | None
amount_usdfloat | None
created_atAny
rawdict[str, Any]

The response JSON exactly as the API sent it.

CardPrograminvite-only beta

class CardProgram

Card issuing for your company. status: not_started, then pending_verification while the card issuer verifies the company (what it still needs is in requirements_due), active; or rejected, or restricted (it was active and needs something again). Your company applies once, in the console at onboarding_url: US-registered businesses, in dollars.

Attributes
statusstr
requirements_duelist[str]
company_namestr | None
countrystr | None
currencystr | None
onboarding_urlstr | None
rawdict[str, Any]

The response JSON exactly as the API sent it.

CardMerchantCategoryinvite-only beta

class CardMerchantCategory

A name merchants takes, and the merchant category codes it covers.

Attributes
namestr
descriptionstr | None
mccslist[str]