Skip to main content

Installation

npm install @actioncodes/sdk

Initialize

import { ActionCodesClient } from '@actioncodes/sdk'

const client = new ActionCodesClient({
  authToken: process.env.ACTION_CODES_TOKEN
})
Auth token required. Request one by DMing @beharefe on Telegram or emailing gm@actioncodes.org.

Methods

resolve

Verify a code and get its details.
const actionCode = await client.resolve(code)
code
string
required
The 8-digit action code from the user
ActionCode
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)
code
string
required
The action code to check
ActionCodeStatusResponse
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
}
code
string
required
The action code to observe
options
object
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?)
code
string
required
The action code
transaction
string
required
Base64-encoded serialized transaction
meta
ActionCodeMeta
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?)
code
string
required
The action code
message
string
required
The message to sign
meta
ActionCodeMeta
Optional metadata
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)
code
string
required
The action code
signature
string
required
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)
code
string
required
The action code
signedMessage
string
required
The signed message

register

Create a new action code. For advanced use — most apps receive codes from users instead.
const actionCode = await client.register(pubkey, sign, metadata?)
pubkey
PublicKey
required
The wallet public key
sign
function
required
A function that signs a message: (message: string) => Promise<string>
metadata
ActionCodeMeta
Optional metadata
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)')
  }
}
ErrorCause
CodeNotFoundErrorThe code doesn’t exist
ExpiredCodeErrorThe code has expired (~2 min lifetime)
InvalidCodeFormatErrorCode is not 8 digits
UnauthorizedErrorPermission denied

Types

ActionCodeMeta

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)
}