
Security News
Rust 1.100 Will Add Dependency Cooldowns to Cargo
Rust 1.100 will add opt-in dependency cooldowns to Cargo, giving security scanners and maintainers time to catch malicious crate releases.
Command-line interface for the Mayar API. Zero runtime dependencies — Node.js 18+ stdlib only.
One-liner (no npm required):
curl -fsSL https://raw.githubusercontent.com/mayarid/mayar-cli/main/install.sh | sh
Downloads the latest tarball from the npm registry, extracts to ~/.local/share/mayar, and symlinks the binary to ~/.local/bin/mayar. Requires only node>=18, curl, and tar. Override paths or pin a version:
MAYAR_VERSION=0.1.9 MAYAR_BIN_DIR=/usr/local/bin \
curl -fsSL https://raw.githubusercontent.com/mayarid/mayar-cli/main/install.sh | sh
Via npm:
npm install -g mayar
# or run without installing:
npx mayar --help
From source:
git clone https://github.com/mayarid/mayar-cli.git
cd mayar-cli
npm link # exposes a `mayar` command on your PATH
Preferred — browser login:
mayar login
Opens your browser for Google OAuth sign-in and saves the auth token automatically — no copy-pasting keys. Use mayar login --no-browser on headless machines to get a URL to open elsewhere.
Alternatives (resolution order: --api-key flag → MAYAR_API_KEY env → saved config file):
Option 1 — environment variable (recommended for CI/agents):
export MAYAR_API_KEY=your_api_key_here
mayar whoami
Option 2 — saved config (interactive first run):
The first time you invoke any command without a key configured, the CLI prompts for your production API key (input is masked). Paste it once; it's stored at ~/.config/mayar/config.json (chmod 600, follows XDG Base Directory) and reused on subsequent runs.
mayar balance
# Welcome to Mayar CLI.
# No API key found. Get yours from https://web.mayar.id → Integration → API Key.
# Paste your production API key: ************
# ✓ Saved to /Users/you/.config/mayar/config.json
Option 3 — non-interactive setup:
mayar api-key YOUR_KEY_HERE
Option 4 — per-command override:
mayar --api-key YOUR_KEY_HERE balance
Get your API key from web.mayar.id → Integration → API Key.
v1.0.0 — Mayar API v2. All commands now target
/hl/v2/.... Pagination switched from--page/--pageSizeto cursor-based--limit/--after(returned in the previous response'snextStartingAfter). The--pageSizeflag is still accepted as an alias for--limit.
Setup
init First-time setup (interactive, masked input)
login [--no-browser] Sign in via Google OAuth
api-key <key> Save API key non-interactively
config show|reset Inspect or remove the saved config
Account
whoami Merchant identity (JWT decode + live verify)
balance GET /hl/v2/balances
Invoices
invoice list [--limit --after --status --search]
invoice get <id>
invoice close <id> | reopen <id> POST /hl/v2/invoices/{id}/{open|close}
invoice status <id> <action> action ∈ open|close|active|closed|unlisted
invoice edit <id> --data <json|@file>
invoice filter --email <email> [--limit --after --status --search]
invoice create --data <json|@file>
Products
product list [--limit --after --search --type --stock]
product search <keyword>
product type <ebook|course|membership|saas|event|webinar|…>
product get <id>
product close | reopen | status <id> <action>
product transactions <id> [--limit --after --status --customerId]
product create --type <T> --data <json|@file>
T ∈ ebook|digital|event|webinar|generic|payment-link
product edit <id> --type <T> --data <json|@file>
product sort <generic_link|event|webinar|digital_product> [--limit --after]
POST /hl/v2/payment-links/sort/{type}
Payment links (alt route)
payment-link edit <id> --data <json|@file>
POST /hl/v2/payment-links/{id}/update
(alias for `product edit --type payment-link`)
Single payment requests
payment list [--limit --after --status]
payment get <id>
payment close | reopen | status <id> <action>
payment edit <id> --data <json|@file>
payment create --data <json|@file>
Customers
customer list GET /hl/v2/customers
customer create --data <json|@file>
customer search <email> GET /hl/v2/customers/detail?email=
customer update <fromEmail> <toEmail>
customer magic-link <email> POST /hl/v2/customers/portal-login
Transactions
tx list [--limit --after --status --customerId --type --startAt --endAt]
tx unpaid [--limit --after --status --customerId --startAt --endAt]
tx daily
tx product <productId> [--limit --after --status]
Reviews
review list [--limit --after --status --paymentLinkId --rating]
review stats [productId] merchant-wide or per-product
review create --data <json|@file>
review update <id> --data <json|@file>
review bulk-status --data <json> [{id,status: ACTIVE|ARCHIVED|INACTIVE}, …]
review product <paymentLinkId> [--limit --after --rating]
review product-customer <paymentLinkId> --customerId <id>
Discounts (coupons)
discount create --data <json|@file>
discount get <id>
discount validate <code> <paymentLinkId>
discount check <code>
Bundling
bundling list [--limit --after]
bundling get <id>
Installments
installment list [--limit --after --status --customerId]
installment get <id>
installment create --data <json|@file>
Memberships (see the Memberships guide below)
membership product create --data <json|@file> {name, description, membershipInfo}
membership product get <productId>
membership tier create --data <json|@file> {productId, name, description, periods[]}
membership tier get <tierId> --productId <id>
membership tiers --productId <id> [--limit --after] list every tier of a product
membership register --data <json|@file>
membership members --productId <id> [--limit --after]
membership get <memberId> --productId <id>
membership update <memberId> --productId <id> [--data <json|@file>]
membership cancel <memberId> --productId <id>
membership create-invoice <memberId> --productId <id>
Credit wallets
credit balance --customerId <id> --productId <id> [--tierId <id>]
credit add --data <json|@file> {customerId, productId, amount}
credit spend --data <json|@file> {customerId, productId, amount}
credit history <customerId> --productId <id> [--page N --limit N]
credit register-usage --data <json|@file>
credit register-membership --data <json|@file>
credit checkout --data <json|@file>
SaaS licensing
saas activate <licenseCode> <productId> POST /saas/v2/license/activate
saas deactivate <licenseCode> <productId> POST /saas/v2/license/deactivate
saas verify <licenseCode> <productId> POST /saas/v2/license/verify
Software licensing
software verify <licenseCode> <productId> POST /software/v2/license/verify
QR & payment channels
qrcode <amount> POST /hl/v2/qr-codes/create
qrcode static GET /hl/v2/qr-codes/static
qrcode channels GET /hl/v2/payment-channels
Webhooks
webhook register <url> POST /hl/v2/webhooks/update
webhook test <url>
webhook history [--limit --after --status --type --startAt --endAt]
webhook new-history [--limit --after]
webhook retry <historyId>
Global flags
--json Output raw JSON instead of pretty tables
--api-key <key> Use this API key for the run (also accepts --api-key=KEY)
--limit N Page size (default 10, max 50)
--after CURSOR Pagination cursor (from previous nextStartingAfter)
--pageSize N Alias for --limit
--data <json|@file> Inline JSON, or @path to a JSON file
-h, --help Show help
-v, --version Show version
Environment
MAYAR_API_KEY Used when --api-key is absent and no config is saved
MAYAR_API_URL Override API base URL
MAYAR_AUTH_URL Override auth server base URL (used by 'login')
NODE_ENV=development Target the sandbox (*.mayar.club) instead of production
# Verify active user / API key
mayar whoami
# Account balance
mayar balance
# Paginated lists (v2 cursor pagination)
mayar invoice list --limit 20
mayar invoice list --limit 20 --after 1730000000000 # next page
mayar product type ebook --limit 50
# Search
mayar product search "kelas python"
# Create an invoice from a JSON file
cat > /tmp/inv.json <<'JSON'
{
"name": "Andre",
"email": "andre@example.com",
"mobile": "08123456789",
"redirectUrl": "https://example.com/thanks",
"description": "Order #1234",
"expiredAt": "2026-12-31T23:59:59.000Z",
"items": [{ "quantity": 1, "rate": 50000, "description": "1x Course" }]
}
JSON
mayar invoice create --data @/tmp/inv.json
# Create a customer inline
mayar customer create --data '{"name":"Raihan","email":"r@example.com","mobile":"081234567890"}'
# Create a payment request
mayar payment create --data '{"name":"X","email":"x@y.com","amount":170000,"mobile":"08123","redirectUrl":"https://m.com","description":"Test","expiredAt":"2026-12-31T00:00:00.000Z"}'
# Dynamic QR for IDR 10,000
mayar qrcode 10000
# Webhooks
mayar webhook register https://example.com/hooks/mayar
mayar webhook test https://example.com/hooks/mayar
mayar webhook history --limit 20
# Membership members
mayar membership members --productId prd-42 --limit 20
# SaaS license verify
mayar saas verify LIC-123 prd-42
# Pipe raw JSON to jq
mayar invoice list --json | jq '.data[] | {id, status}'
A membership in Mayar is three nested objects. Create them in this order — each level needs the id of the one above it:
membership product the subscription product itself ("Kelas Premium")
└── tier a plan inside it ("Basic", "Pro") — carries the pricing
└── period one billing option of that tier (1 month, 12 months, lifetime)
└── member a customer subscribed to that tier
mayar membership product create --data '{
"name": "Kelas Premium",
"description": "Akses penuh ke seluruh materi",
"membershipInfo": { "showMembers": true, "type": "MEMBERSHIP" }
}'
Returns the product; keep its id — every command below needs it as --productId.
| Field | Required | Notes |
|---|---|---|
name | yes | Product name shown to buyers |
description | yes | Product description |
redirectUrl | no | Where the buyer lands after a successful payment |
coverImage | no | Cover image URL |
hidePortalAccessInEmails | no | true to omit the member-portal link from transactional emails |
membershipInfo | yes | Object — see below |
membershipInfo fields:
| Field | Required | Notes |
|---|---|---|
showMembers | yes | true to list members publicly on the product page |
type | yes | Uppercase enum: MEMBERSHIP, SAAS, or CREDIT. Anything else is rejected |
creditValue | no | Credits granted per billing cycle (CREDIT type) |
enableCreditTopup | no | Let members buy extra credit |
isAccumulateCredit | no | Unused credit rolls over to the next cycle instead of resetting |
isAccumulateTopupCredit | no | Same rollover rule, for topped-up credit |
minCreditTopup | no | Minimum credit per top-up |
maxCreditTopup | no | Maximum credit per top-up |
The creditValue/*Credit* fields only do anything when type is CREDIT — omit them for a plain MEMBERSHIP.
A tier holds the prices. periods is the list of billing options a buyer can pick, so a tier with monthly and annual pricing is one tier with two periods — not two tiers.
mayar membership tier create --data '{
"productId": "prd-42",
"name": "Pro",
"description": "Semua materi + sesi live bulanan",
"trialPeriodInDays": 7,
"isTrialAvailable": true,
"periods": [
{ "monthPeriod": 1, "amount": 99000, "status": "ACTIVE" },
{ "monthPeriod": 12, "amount": 990000, "status": "ACTIVE" },
{ "isLifetime": true, "amount": 2500000, "status": "ACTIVE" }
]
}'
| Field | Required | Notes |
|---|---|---|
productId | yes | Id from step 1 — passed in the body, not as a flag |
name | yes | Tier name |
description | yes | Tier description |
notes | no | Internal note, not shown to buyers |
limit | no | Cap on how many members may join this tier |
upfrontFee | no | One-off joining fee charged on top of the first period |
finishMembershipAt | no | ISO date when the membership ends regardless of billing |
gracePeriodInDays | no | Days of continued access after a missed payment |
trialPeriodInDays | no | Free-trial length |
trialCredit | no | Credits granted during the trial (CREDIT products) |
isTrialAvailable | no | Set true to actually enable the trial |
redirectUrl | no | Post-checkout redirect for this tier |
periods | yes | Array — at least one entry |
Each periods[] entry:
| Field | Notes |
|---|---|
monthPeriod | Billing cycle in months (1 = monthly, 12 = yearly). Omit when isLifetime |
amount | Price in IDR for this cycle |
credit | Credits granted per cycle (CREDIT products) |
isLifetime | true for a one-time payment with no renewal |
status | ACTIVE to sell it, otherwise it stays hidden |
# Who can they subscribe to?
mayar membership tiers --productId prd-42
# Subscribe a customer to a tier — the body identifies the product, the tier, and the buyer.
# Run `mayar docs membership` for the authoritative member field list.
mayar membership register --data '{
"productId": "prd-42",
"membershipTierId": "tier-7",
"name": "Andre",
"email": "andre@example.com",
"mobile": "08123456789"
}'
# Then, per member
mayar membership members --productId prd-42 --limit 20
mayar membership get <memberId> --productId prd-42
mayar membership update <memberId> --productId prd-42 --data '{"membershipTierId":"tier-9"}'
mayar membership create-invoice <memberId> --productId prd-42 # bill the next cycle now
mayar membership cancel <memberId> --productId prd-42
mayar membership product get prd-42
mayar membership tier get tier-7 --productId prd-42 # --productId is required here
mayar membership tiers --productId prd-42 --json | jq '.data[] | {id, name, amount}'
Two traps worth remembering: tiers (plural) lists a product's tiers while tier (singular) is the create/get sub-namespace, and tier get needs --productId even though the tier id alone looks sufficient. Every write command takes --data as either inline JSON or @path/to/file.json.
| Key | Value |
|---|---|
| Path | $XDG_CONFIG_HOME/mayar/config.json, defaulting to ~/.config/mayar/config.json (chmod 600) |
| Endpoint | MAYAR_API_URL env or https://api.mayar.id (default) |
Legacy installs that wrote to ~/.mayar/config.json are migrated automatically on first run.
To rotate keys: mayar config reset && mayar init.
Authorization: Bearer <key>. Errors print API <status> — <message> and exit non-zero.--data @file.json reads from disk; --data '{...}' reads inline JSON.MAYAR_API_URL overrides the base URL — useful for staging or custom proxy environments.mayar whoami decodes the JWT locally and verifies the key live against /hl/v2/balances./hl/v1/* to /hl/v2/*. --page is no longer used (v2 has no page numbers); the cursor is returned as nextStartingAfter in each response and passed as --after for the next page.FAQs
Node.js CLI for the Mayar API (production endpoint).
We found that mayar demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.

Security News
Rust 1.100 will add opt-in dependency cooldowns to Cargo, giving security scanners and maintainers time to catch malicious crate releases.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.

Security News
Anthropic’s free OSS Scanner uses Mythos and other Claude models to find open source vulnerabilities and send maintainers reports with proposed fixes.