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 CardA 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.
- Like every live object, it also has hydrate(), refresh() and raw.
| id | str | |
| name | str | |
| last4 | str | None | |
| brand | str | None | The card network, like |
| exp_month | int | None | |
| exp_year | int | None | |
| status | str |
|
| status_reason | str | None | Why it's frozen when nobody froze it: |
| limit_usd | float | None | The most it spends per |
| interval | str | None | What |
| spent_usd | float | None | Spent in the current interval (this calendar month's spend for a per-transaction limit). |
| resets_at | Any | When |
| merchants | list[str] | None | The merchant categories it may pay (None: any). |
| require_approval_above_usd | float | None | Charges above this wait for a person (None: never). |
| agent_id | str | None | The agent that pays with it (None if none). |
| single_use | bool | None | Whether every checkout gets a fresh number that takes one charge ( |
| user_id | str | None | |
| created_at | Any |
createclassmethodasync
noclick.Card.create(
name, *, limit_usd, interval='month', merchants=None, agent=None,
require_approval_above_usd=None, single_use=False, user=..., client=None
) -> CardIssue 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).
'month'NoneNoneNoneFalseWho the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)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.
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
) -> CardA 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().
FalseNone'month'NoneNoneWho the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)A card that loads (or is issued, or brought in step with your definition) the first time it's used. Nothing is sent yet.
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=...) -> CardA lazy reference to a card by id.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
A card that loads the first time you read a field. Nothing is sent yet.
card = noclick.Card.from_id("c4a7e2b9-3d1f-4a8e-b6c5-9f2d0e7a1b36")
card.freeze()getclassmethodasync
noclick.Card.get(card_id, *, user=..., client=None) -> CardLoad a card by id now; an unknown id raises noclick.NotFound.
Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)The card, loaded, with this interval's spent_usd.
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.
NoneNoneReturn at most this many.
None)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.
None)Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)Every matching card, newest first, fetched page by page. Closed cards are listed only with status="closed".
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
) -> PageOne page of cards; pass next_cursor back as cursor.
The next_cursor of the page before; omit it for the first page.
None)Return at most this many.
None)NoneNoneWhich users a read covers: noclick.ALL_USERS, noclick.NO_USER, a User, or your id for one. It filters without acting as that user.
None)Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)A page whose data holds the cards and whose next_cursor is None on the last page.
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) -> CardFundingYour company's card balance, which every card (the account's and every user's) spends from.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)The balance_usd, what pending charges hold (pending_usd), what cards can still spend
(available_usd), and the bank_transfer details that fund it.
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) -> CardProgramWhere your company's card issuing stands. It's applied for once, in the console.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)The program's status (not_started, pending_verification, active, rejected or
restricted), what it still needs (requirements_due) and where to apply (onboarding_url).
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).
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)Every category, like office_supplies, software and travel.
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.
NoneReturn at most this many.
20)The newest limit (20 by default; None for every one), each with its amount,
merchant, status, for a decline why, and any dispute.
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() -> CardDetailsA 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.
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.
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 invoicepurchaseasync
card.purchase(amount_usd, merchant, *, reason=None) -> OperationApprove 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.
NoneA 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).
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
) -> CardDisputeDispute 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.
NoneNoneNoneThe dispute, submitted; card.dispute.updated events follow it to won or lost.
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) # submittedupdateasync
card.update(
*, name=..., limit_usd=..., interval=..., merchants=..., agent=...,
require_approval_above_usd=..., single_use=...
) -> CardChange 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.
This card, updated. A field you leave out is unchanged, and changes apply from the next charge.
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 onfreezeasync
card.freeze() -> CardDecline every charge until unfreeze(); the card keeps its number and limits.
This card, frozen.
card = noclick.Card.from_name("Travel", user="user_42")
card.freeze()
print(card.status) # frozenunfreezeasync
card.unfreeze() -> CardLet 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.
This card, active.
card = noclick.Card.from_name("Travel", user="user_42")
card.unfreeze()
print(card.status) # activecloseasync
card.close() -> CardClose the card for good: it declines every charge from now on and its number is never reissued. Its transactions stay readable.
This card, closed.
card = noclick.Card.from_name("Software")
card.close()
print(card.status) # closedCardTransactioninvite-only beta
class CardTransactionOne 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.
| id | str | None | |
| card_id | str | None | |
| kind | str | None | |
| amount_usd | float | None | |
| settled_amount_usd | float | None | |
| merchant | str | None | |
| merchant_category | str | None | The |
| merchant_mcc | str | None | |
| status | str | None | |
| decline_reason | str | None | |
| approval_id | str | None | The held charge's approval, an operation: |
| purchase_id | str | None | The |
| last4 | str | None | The number it was on (a single-use card has one per checkout). |
| dispute | CardDispute | None | |
| at | Any | |
| raw | dict[str, Any] | The response JSON exactly as the API sent it. |
CardDetailsinvite-only beta
class CardDetailsA 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.
| url | str | |
| expires_at | Any | |
| last4 | str | None | |
| brand | str | None | |
| exp_month | int | None | |
| exp_year | int | None | |
| single_use | bool | None | A number that takes one charge (every number of a single-use card, every purchase's). |
| amount_usd | float | None | A purchase's exact amount: the most its one charge may be. |
| closes_at | Any | When a single-use number nobody paid with closes. |
| cardholder | str | None | The name and billing address a checkout asks for: your company's. |
| billing_address | dict[str, Any] | None | |
| raw | dict[str, Any] | The response JSON exactly as the API sent it. |
CardFundinginvite-only beta
class CardFundingYour 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).
| balance_usd | float | None | |
| pending_usd | float | None | |
| available_usd | float | None | What cards can still spend: the balance less what's pending. |
| top_up_url | str | None | The console page with these bank details. |
| bank_transfer | CardBankTransfer | None | |
| frozen_since | Any | Since when your cards have been frozen because the balance is below zero (None: they aren't). |
| raw | dict[str, Any] | The response JSON exactly as the API sent it. |
CardBankTransferinvite-only beta
class CardBankTransferWhere your company sends money to fund its card balance: a wire lands the same day, an ACH transfer within a few business days.
| bank_name | str | None | |
| routing_number | str | None | |
| account_number | str | None | |
| networks | list[str] |
|
CardDisputeinvite-only beta
class CardDisputeA settled charge disputed with the card network. status is submitted,
then won (the money comes back to your card balance), lost or expired.
| id | str | None | |
| transaction_id | str | None | |
| reason | str | None |
|
| status | str | None | |
| amount_usd | float | None | |
| created_at | Any | |
| raw | dict[str, Any] | The response JSON exactly as the API sent it. |
CardPrograminvite-only beta
class CardProgramCard 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.
| status | str | |
| requirements_due | list[str] | |
| company_name | str | None | |
| country | str | None | |
| currency | str | None | |
| onboarding_url | str | None | |
| raw | dict[str, Any] | The response JSON exactly as the API sent it. |
CardMerchantCategoryinvite-only beta
class CardMerchantCategoryA name merchants takes, and the merchant category codes it covers.
| name | str | |
| description | str | None | |
| mccs | list[str] |