Installation
npm install @actioncodes/sdk
Initialize
import { ActionCodesClient } from '@actioncodes/sdk'
const client = new ActionCodesClient ({
authToken: process . env . ACTION_CODES_TOKEN
})
Methods
resolve
Verify a code and get its details.
const actionCode = await client . resolve ( code )
The 8-digit action code from the user
The wallet public key this code belongs to
Unix timestamp when the code expires
Current status: pending, attached, finalized, or expired
Example:
const actionCode = await client . resolve ( '48291037' )
console . log ( 'Wallet:' , actionCode . pubkey )
console . log ( 'Expires:' , new Date ( actionCode . expiresAt ))
console . log ( 'Status:' , actionCode . status )
getStatus
Get the current status of a code.
const status = await client . getStatus ( code )
pending | attached | finalized | expired
Whether a transaction is attached
Whether a message is attached
Transaction signature (if finalized with transaction)
Signed message bytes (if finalized with message)
Example:
const status = await client . getStatus ( '48291037' )
if ( status . finalizedSignature ) {
console . log ( 'Transaction signed:' , status . finalizedSignature )
}
observeStatus
Watch for status changes over time. Returns an async iterator.
for await ( const status of client . observeStatus ( code , options )) {
// Handle status updates
}
The action code to observe
Polling interval in milliseconds
Maximum time to observe in milliseconds
Example:
for await ( const status of client . observeStatus ( '48291037' , { interval: 1000 })) {
console . log ( 'Status:' , status . status )
if ( status . finalizedSignature ) {
console . log ( 'Done! Signature:' , status . finalizedSignature )
break
}
if ( status . signedMessage ) {
console . log ( 'Done! Signed message:' , status . signedMessage )
break
}
if ( status . status === 'expired' ) {
console . log ( 'Code expired' )
break
}
}
observeStatus automatically stops when the code is finalized or expires. You can also break early.
attachTransaction
Attach a transaction for the user to sign.
await client . attachTransaction ( code , transaction , meta ? )
Base64-encoded serialized transaction
Optional metadata (description, label, etc.)
Example:
import { Transaction , SystemProgram , PublicKey , Connection } from '@solana/web3.js'
// Build the transaction
const connection = new Connection ( 'https://api.mainnet-beta.solana.com' )
const { blockhash } = await connection . getLatestBlockhash ()
const tx = new Transaction ({
recentBlockhash: blockhash ,
feePayer: new PublicKey ( actionCode . pubkey )
}). add (
SystemProgram . transfer ({
fromPubkey: new PublicKey ( actionCode . pubkey ),
toPubkey: new PublicKey ( recipient ),
lamports: amount
})
)
// Serialize (without signatures) and attach
const serialized = tx . serialize ({ requireAllSignatures: false }). toString ( 'base64' )
await client . attachTransaction ( code , serialized , {
description: 'Transfer 0.1 SOL'
})
attachMessage
Attach a message for the user to sign.
await client . attachMessage ( code , message , meta ? )
Example:
await client . attachMessage ( code , 'Sign in to MyApp at ' + Date . now (), {
description: 'Sign-in verification'
})
finalizeTransaction
Manually finalize a code with a transaction signature. Usually not needed — the user’s wallet does this automatically.
await client . finalizeTransaction ( code , signature )
The transaction signature
finalizeMessage
Manually finalize a code with a signed message. Usually not needed — the user’s wallet does this automatically.
await client . finalizeMessage ( code , signedMessage )
register
Create a new action code. For advanced use — most apps receive codes from users instead.
const actionCode = await client . register ( pubkey , sign , metadata ? )
A function that signs a message: (message: string) => Promise<string>
Example:
import { PublicKey } from '@solana/web3.js'
const actionCode = await client . register (
new PublicKey ( walletAddress ),
async ( message ) => {
// Sign with your wallet
return wallet . signMessage ( message )
},
{ description: 'My action code' }
)
console . log ( 'Generated code:' , actionCode . code )
Most applications should receive codes from users rather than generate them. Users generate codes at actioncode.app .
Error Handling
The SDK throws specific error types:
import {
CodeNotFoundError ,
ExpiredCodeError ,
InvalidCodeFormatError ,
UnauthorizedError
} from '@actioncodes/sdk'
try {
await client . resolve ( code )
} catch ( error ) {
if ( error instanceof CodeNotFoundError ) {
console . log ( 'Code does not exist' )
} else if ( error instanceof ExpiredCodeError ) {
console . log ( 'Code has expired' )
} else if ( error instanceof InvalidCodeFormatError ) {
console . log ( 'Invalid code format (must be 8 digits)' )
}
}
Error Cause CodeNotFoundErrorThe code doesn’t exist ExpiredCodeErrorThe code has expired (~2 min lifetime) InvalidCodeFormatErrorCode is not 8 digits UnauthorizedErrorPermission denied
Types
interface ActionCodeMeta {
description ?: string // Human-readable description
label ?: string // Short label
memo ?: string // Additional memo
}
ActionCodeStatusResponse
interface ActionCodeStatusResponse {
status : 'pending' | 'attached' | 'finalized' | 'expired'
expiresAt : number
hasTransaction : boolean
hasMessage : boolean
finalizedSignature ?: string
signedMessage ?: string
}
ObserveStatusOptions
interface ObserveStatusOptions {
interval ?: number // Polling interval in ms (default: 2000)
timeout ?: number // Max observation time in ms (default: 120000)
}