Skip to main content

Subscriptions

List subscriptions

GET /v0/subscriptions/:merchant

Returns all subscription allocations across all modules owned by the merchant.

Auth: API key or SIWE session

Path parameters:

ParameterTypeDescription
merchantstringMerchant wallet address (0x…)

Query parameters:

ParameterTypeRequiredDescription
chain_idnumberNoChain ID to filter. Defaults to the indexer's configured chain.

Response fields:

FieldTypeDescription
allocation_idstringInternal allocation identifier
on_chain_idstringOn-chain subscription ID
module_addressstringSubscriptionModule contract address
user_addressstringSubscriber wallet address
plan_idstringInternal plan identifier
statusstringComputed status (see Status values)
total_spentstringCumulative amount charged, in token base units
times_executednumberNumber of successful payment executions
next_charge_datestring | nullISO 8601 timestamp of the next scheduled charge, or null if no charge is queued
expires_atstring | nullPermit2 allowance expiry timestamp
remaining_executionsnumber | nullRemaining execution budget, or null for unlimited
created_atstringISO 8601 creation timestamp
is_blockedbooleanWhether the subscriber is blocked by the merchant

Example request:

const res = await fetch(
'https://api.amser.io/v0/subscriptions/0xMerchantAddress?chain_id=84532',
{
headers: {
'Authorization': `Bearer ${process.env.AMSER_API_KEY}`,
},
}
);
const subscriptions = await res.json();

Example response:

[
{
"allocation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"on_chain_id": "1",
"module_address": "0xabcdef1234567890abcdef1234567890abcdef12",
"user_address": "0x1111111111111111111111111111111111111111",
"plan_id": "plan-uuid-here",
"status": "ACTIVE",
"total_spent": "10000000",
"times_executed": 3,
"next_charge_date": "2026-04-01T00:00:00.000Z",
"expires_at": "1743465600",
"remaining_executions": null,
"created_at": "2026-01-01T00:00:00.000Z",
"is_blocked": false
}
]

Status values

The status field is computed at query time by the indexer, not stored directly. It mirrors the on-chain _isActive() logic.

StatusMeaning
ACTIVESubscription is active and at least one payment has been executed
PENDINGSubscription exists on-chain but the first payment has not yet executed (times_executed === 0). The keeper will execute the first charge within one poll interval.
PAUSEDSubscriber or merchant paused the subscription. No charges will execute until resumed.
CANCELLEDSubscriber cancelled. The allocation remains but is inactive.
EXPIREDThe access window has lapsed — now > lastPaidAt + interval + gracePeriod. The user still has an allocation, but it is not considered active.
BLOCKEDThe merchant has blocked this subscriber. No charges will execute.

Get subscription by ID

GET /v0/subscriptions/:merchant/:id

Returns a single subscription by its on-chain ID.

Auth: API key or SIWE session

Path parameters:

ParameterTypeDescription
merchantstringMerchant wallet address (0x…)
idstringOn-chain subscription ID

The response shape is identical to a single element from the list endpoint.


Check authorization

GET /v0/auth/check

The primary endpoint for checking whether a wallet is currently authorized for one or more plans on a given module. This is the recommended way to gate access in your application without requiring a wallet library or RPC infrastructure on your server.

Auth: None required (public endpoint)

Query parameters:

ParameterTypeRequiredDescription
module_addressstringYesSubscriptionModule contract address (0x…)
walletstringYesWallet address to check (0x…)
plan_idsstringYesComma-separated plan IDs (uint32 values)
modestringNoindexed, onchain, or both. Default: indexed
chain_idnumberNoChain ID. Defaults to the indexer's configured chain.

Modes

indexed — Queries the indexer database. Fast (~5ms), free, and sufficient for most access control decisions. The result reflects the most recent events the indexer has processed, which may lag a few seconds behind the chain.

onchain — Calls isActive() (single plan) or isActiveAny() (multiple plans) directly on the contract. Authoritative and trustless, but costs an RPC call and is slightly slower. Use this for high-stakes checks or when you need certainty immediately after a state change.

both — Runs indexed and on-chain checks in parallel and returns both results. The top-level authorized field uses the on-chain result. Useful during development to compare the two approaches or to get fast indexed data with authoritative confirmation.

info

authorized: true via indexed mode means the indexer believes the subscription is active based on the most recent events it has processed. In the seconds immediately after a new subscription or payment, the indexed result may briefly lag behind the on-chain state.

Response shape

FieldTypeDescription
authorizedbooleanWhether the wallet is authorized for any of the requested plans
modestringThe mode used for this check
walletstringThe wallet address checked
module_addressstringThe module address checked
chain_idnumberThe chain ID
plan_idsstring[]The plan IDs checked
indexedobject | undefinedPresent when mode is indexed or both
indexed.authorizedbooleanIndexed authorization result
indexed.matching_plan_idsstring[]Plan IDs with active paid subscriptions
indexed.detailsarrayPer-allocation details (see below)
onchainobject | undefinedPresent when mode is onchain or both
onchain.authorizedbooleanOn-chain authorization result
onchain.methodstring"isActive" (single plan) or "isActiveAny" (multiple plans)

Each entry in indexed.details:

FieldTypeDescription
allocation_idstringAllocation identifier
plan_id_on_chainstringOn-chain plan ID
statusstringCurrent status
next_charge_datestring | nullNext scheduled charge
times_executednumberNumber of successful payments

Example: indexed mode

const res = await fetch(
'https://api.amser.io/v0/auth/check?' +
'module_address=0xModuleAddress&' +
'wallet=0xSubscriberWallet&' +
'plan_ids=1&' +
'mode=indexed'
);
const data = await res.json();
{
"authorized": true,
"mode": "indexed",
"wallet": "0xsubscriberaddress",
"module_address": "0xmoduleaddress",
"chain_id": 84532,
"plan_ids": ["1"],
"indexed": {
"authorized": true,
"matching_plan_ids": ["1"],
"details": [
{
"allocation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"plan_id_on_chain": "1",
"status": "ACTIVE",
"next_charge_date": "2026-04-01T00:00:00.000Z",
"times_executed": 3
}
]
}
}

Example: onchain mode

const res = await fetch(
'https://api.amser.io/v0/auth/check?' +
'module_address=0xModuleAddress&' +
'wallet=0xSubscriberWallet&' +
'plan_ids=1&' +
'mode=onchain'
);
const data = await res.json();
{
"authorized": true,
"mode": "onchain",
"wallet": "0xsubscriberaddress",
"module_address": "0xmoduleaddress",
"chain_id": 84532,
"plan_ids": ["1"],
"onchain": {
"authorized": true,
"method": "isActive"
}
}

Example: both mode

const res = await fetch(
'https://api.amser.io/v0/auth/check?' +
'module_address=0xModuleAddress&' +
'wallet=0xSubscriberWallet&' +
'plan_ids=1,2&' +
'mode=both'
);
const data = await res.json();
{
"authorized": true,
"mode": "both",
"wallet": "0xsubscriberaddress",
"module_address": "0xmoduleaddress",
"chain_id": 84532,
"plan_ids": ["1", "2"],
"indexed": {
"authorized": true,
"matching_plan_ids": ["1"],
"details": [
{
"allocation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"plan_id_on_chain": "1",
"status": "ACTIVE",
"next_charge_date": "2026-04-01T00:00:00.000Z",
"times_executed": 3
}
]
},
"onchain": {
"authorized": true,
"method": "isActiveAny"
}
}

When multiple plan IDs are provided, the onchain mode uses isActiveAny() instead of isActive(), returning true if the wallet is active on any of the specified plans.

Server-side auth check without a wallet

The most common integration pattern is a server-side subscription check using only an API key — no wallet library, no private key, no RPC endpoint:

async function checkSubscription(
walletAddress: string,
moduleAddress: string,
planId: number
): Promise<boolean> {
const res = await fetch(
`https://api.amser.io/v0/auth/check?` +
`module_address=${moduleAddress}&` +
`wallet=${walletAddress}&` +
`plan_ids=${planId}&` +
`mode=indexed`,
{
headers: {
'Authorization': `Bearer ${process.env.AMSER_API_KEY}`,
}
}
);

const { authorized } = await res.json();
return authorized;
}