otpiq
Version:
TypeScript client for the OTPiq SMS verification service
482 lines (386 loc) • 12.7 kB
Markdown
# OTPiq Client
A TypeScript/JavaScript client for the OTPiq SMS service. This package provides a clean and type-safe way to interact with the OTPiq API, supporting verification codes, custom messages, sender IDs, and message tracking.
[](https://www.npmjs.com/package/otpiq)
[](https://github.com/ellerbrock/typescript-badges/)
## Features
- 📱 SMS verification code sending with auto-generation
- 💬 Custom message support with sender IDs
- 🎲 Automatic or custom verification code generation
- ✨ Multi-provider support: SMS, WhatsApp, Telegram
- ✅ Full TypeScript support with strict typing
- 🔄 Real-time SMS delivery tracking
- 💳 Credit balance and spending management
- ⚡ Promise-based API with async/await
- 🛡️ Comprehensive error handling with specific error types
- 📊 Carrier-specific pricing information
- 🚦 Built-in rate limit handling
- 💰 Spending threshold protection
- 🔔 Webhook support for real-time delivery notifications
## Installation
```bash
npm install otpiq
# or
yarn add otpiq
# or
pnpm add otpiq
```
## Quick Start
```typescript
import { OTPiqClient } from "otpiq";
// Initialize the client
const client = new OTPiqClient({
apiKey: "your_api_key_here",
});
// Send verification SMS with auto-generated code
const response = await client.sendSMS({
phoneNumber: "9647701234567",
smsType: "verification",
});
console.log("Generated code:", response.verificationCode);
console.log("SMS ID:", response.smsId);
console.log("Cost:", response.cost, "IQD");
console.log("Remaining credit:", response.remainingCredit, "IQD");
// Send custom message with sender ID
const customResponse = await client.sendSMS({
phoneNumber: "9647701234567",
smsType: "custom",
customMessage: "Welcome to our service!",
senderId: "MyBrand",
});
```
## Handling Verification Codes
### Auto-generated Codes
When no verification code is provided, the client automatically generates one:
```typescript
const response = await client.sendSMS({
phoneNumber: "9647701234567",
smsType: "verification",
digitCount: 6, // Optional: customize length (default: 6)
});
const generatedCode = response.verificationCode;
```
### Custom Verification Codes
You can provide your own verification code:
```typescript
const response = await client.sendSMS({
phoneNumber: "9647701234567",
smsType: "verification",
verificationCode: "123456",
});
```
## Sending Custom Messages
Custom messages require a sender ID and will automatically use the SMS provider:
```typescript
const response = await client.sendSMS({
phoneNumber: "9647701234567",
smsType: "custom",
customMessage: "Your appointment is confirmed for tomorrow at 2 PM",
senderId: "MyBrand",
});
```
## Managing Sender IDs
Retrieve all your approved sender IDs:
```typescript
const senderIds = await client.getSenderIds();
console.log("Available sender IDs:", senderIds.data);
```
## Next.js Example
Here's a complete Next.js API route implementation:
```typescript
// pages/api/send-verification.ts
import { OTPiqClient } from "otpiq";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== "POST") {
return res.status(405).json({ message: "Method not allowed" });
}
const client = new OTPiqClient({
apiKey: process.env.OTPIQ_API_KEY!,
});
try {
const response = await client.sendSMS({
phoneNumber: req.body.phoneNumber,
smsType: "verification",
});
// Store the code securely (e.g., in your database or session)
await storeVerificationCode({
phoneNumber: req.body.phoneNumber,
code: response.verificationCode,
smsId: response.smsId,
});
res.status(200).json({
message: "Verification code sent",
smsId: response.smsId,
});
} catch (error) {
if (error instanceof Error) {
res.status(500).json({ error: error.message });
} else {
res.status(500).json({ error: "An unknown error occurred" });
}
}
}
```
## API Reference
### Initialization
```typescript
const client = new OTPiqClient({
apiKey: "your_api_key_here",
});
```
### Methods
#### Send SMS
```typescript
// Verification SMS with auto-generated code
const response = await client.sendSMS({
phoneNumber: "9647701234567",
smsType: "verification",
digitCount: 6, // Optional
provider: "auto", // Optional: 'auto' | 'sms' | 'whatsapp' | 'telegram'
});
// Verification SMS with custom code
const response = await client.sendSMS({
phoneNumber: "9647701234567",
smsType: "verification",
verificationCode: "123456",
provider: "whatsapp", // Optional
});
// Custom message with sender ID (always uses SMS provider)
const response = await client.sendSMS({
phoneNumber: "9647701234567",
smsType: "custom",
customMessage: "Your message here",
senderId: "MyBrand",
});
```
#### Convenience Methods
```typescript
// Send via WhatsApp
const response = await client.sendWhatsApp({
phoneNumber: "9647701234567",
smsType: "verification",
verificationCode: "123456", // Optional
});
// Send via Telegram
const response = await client.sendTelegram({
phoneNumber: "9647701234567",
smsType: "verification",
});
// Send custom message (simplified method)
const response = await client.sendCustomMessage({
phoneNumber: "9647701234567",
customMessage: "Your order is ready for pickup!",
senderId: "MyBrand",
});
// Get just the credit balance
const credits = await client.getCredits();
console.log(`Available credits: ${credits} IQD`);
// Alias for trackSMS
const status = await client.getSMSStatus("sms-1234567890");
```
#### Track SMS Status
```typescript
const status = await client.trackSMS("sms-1234567890");
console.log("Delivery status:", status.status);
console.log("Cost:", status.cost);
```
#### Get Project Info
```typescript
const info = await client.getProjectInfo();
console.log("Project name:", info.projectName);
console.log("Available credits:", info.credit);
```
#### Get Sender IDs
```typescript
const senderIds = await client.getSenderIds();
console.log("Available sender IDs:", senderIds.data);
```
### Type Definitions
#### SendSMSOptions
```typescript
interface SendSMSOptions {
phoneNumber: string;
smsType: "verification" | "custom";
verificationCode?: string | number;
customMessage?: string;
senderId?: string;
provider?: "auto" | "sms" | "whatsapp" | "telegram";
digitCount?: number;
deliveryReport?: DeliveryReport;
}
```
#### DeliveryReport
```typescript
interface DeliveryReport {
webhookUrl: string;
deliveryReportType?: "all" | "final";
webhookSecret?: string;
}
```
#### SMSResponse
```typescript
interface SMSResponse {
message: string;
smsId: string;
remainingCredit: number;
cost: number;
canCover: boolean;
paymentType: "prepaid" | "postpaid";
verificationCode?: string; // Only included for verification SMS
}
```
#### SMSTrackingResponse
```typescript
interface SMSTrackingResponse {
status: "pending" | "sent" | "delivered" | "failed" | "expired";
phoneNumber: string;
smsId: string;
cost: number;
}
```
#### SenderId
```typescript
interface SenderId {
_id: string;
senderId: string;
status: "accepted" | "pending" | "rejected";
pricePerSms: {
korekTelecom: number;
asiaCell: number;
zainIraq: number;
others: number;
};
}
```
## Webhooks
Configure webhooks to receive real-time delivery status updates for your messages:
### Basic Webhook Setup
```typescript
const response = await client.sendSMS({
phoneNumber: "9647701234567",
smsType: "verification",
deliveryReport: {
webhookUrl: "https://your-app.com/webhooks/sms-status",
deliveryReportType: "all", // or "final" for final status only
webhookSecret: "your_secret_123" // optional, for security
}
});
```
### Webhook Payload Structure
Your webhook endpoint will receive POST requests with the following payload:
```typescript
interface WebhookPayload {
smsId: string;
deliveryReportType: "all" | "final";
isFinal: boolean;
channel: "sms" | "whatsapp" | "telegram";
status: "sent" | "delivered" | "failed";
senderId?: string; // Only for SMS with custom sender IDs
reason?: string; // Only when status is 'failed'
}
```
### Example Webhook Implementation (Express.js)
```typescript
app.post('/webhooks/sms-status', (req, res) => {
const payload: WebhookPayload = req.body;
const webhookSecret = req.headers['x-otpiq-webhook-secret'];
// Verify webhook secret if configured
if (webhookSecret !== process.env.WEBHOOK_SECRET) {
return res.status(401).send('Unauthorized');
}
// Process the webhook
console.log(`SMS ${payload.smsId} status: ${payload.status}`);
if (payload.status === 'delivered') {
// Handle successful delivery
} else if (payload.status === 'failed') {
console.error(`Delivery failed: ${payload.reason}`);
// Handle failure
}
// Always respond quickly (within 10 seconds)
res.status(200).send('OK');
});
```
### Webhook Configuration Options
- **webhookUrl**: HTTPS URL to receive status updates (required)
- **deliveryReportType**:
- `"all"` - Receive all status updates (sent, delivered, failed)
- `"final"` - Receive only final status (delivered or failed)
- **webhookSecret**: Optional secret for webhook authentication
### Security Best Practices
1. Always use HTTPS for webhook URLs
2. Implement webhook secret validation
3. Respond within 10 seconds to avoid timeouts
4. Implement idempotency to handle duplicate webhooks
5. Log all webhook requests for debugging
## Error Handling
The package includes built-in error classes for common API errors:
```typescript
import {
OTPiqError,
InsufficientCreditError,
RateLimitError,
SpendingThresholdError,
SenderIdError,
TrialModeError,
ValidationError,
NotFoundError,
UnauthorizedError
} from "otpiq";
try {
await client.sendSMS({
phoneNumber: "9647701234567",
smsType: "verification",
});
} catch (error) {
if (error instanceof InsufficientCreditError) {
console.log(
`Need more credits! Required: ${error.requiredCredit}, ` +
`Available: ${error.yourCredit}, Can cover: ${error.canCover}`
);
} else if (error instanceof RateLimitError) {
console.log(
`Rate limit exceeded. Try again in ${error.waitMinutes} minutes. ` +
`Limit: ${error.maxRequests} requests per ${error.timeWindowMinutes} minutes`
);
} else if (error instanceof SpendingThresholdError) {
console.log(
`Spending threshold exceeded! Current: ${error.currentSpending}, ` +
`Threshold: ${error.spendingThreshold}, Transaction cost: ${error.cost}`
);
} else if (error instanceof TrialModeError) {
console.log("Trial mode: Can only send to your own phone number");
} else if (error instanceof SenderIdError) {
console.log("Sender ID error:", error.message);
} else if (error instanceof ValidationError) {
console.log("Validation error:", error.message);
} else if (error instanceof NotFoundError) {
console.log("Not found:", error.message);
} else if (error instanceof UnauthorizedError) {
console.log("Unauthorized:", error.message);
} else if (error instanceof OTPiqError) {
console.log("API Error:", error.message);
}
}
```
## Best Practices
1. **Error Handling**: Always implement proper error handling to catch and handle specific error types.
2. **Verification Codes**: Never send verification codes back to the client. Store them securely server-side.
3. **Provider Selection**:
- Use 'auto' provider for verification codes to let the system choose the best option
- Custom messages always use SMS provider (enforced by the API)
- Use WhatsApp/Telegram for verification codes when targeting specific platforms
4. **Custom Messages**: Always use an approved sender ID when sending custom messages.
5. **Environment Variables**: Store your API key in environment variables, never hardcode it.
6. **Rate Limiting**: Implement client-side rate limiting to avoid hitting API limits.
7. **Credit Monitoring**: Regularly check credit balance and implement alerts for low balance.
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
## License
MIT
## Support
For support:
1. [Create an issue](https://github.com/yadacoder/otpiq/issues) on GitHub
2. Contact support at info.com