
Research
Two Malicious Rust Crates Impersonate Popular Logger to Steal Wallet Keys
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
domainly-sdk
Advanced tools
Official TypeScript SDK for Domainly External SaaS API - Manage multi-tenant custom domains with ease
Official TypeScript/JavaScript SDK for the Domainly External SaaS API. Easily manage multi-tenant custom domains, subdomains, and SSL certificates for your SaaS application.
npm install domainly-sdk
yarn add domainly-sdk
pnpm add domainly-sdk
import { DomainlyClient } from 'domainly-sdk';
// Initialize the client
const client = new DomainlyClient({
apiKey: 'dom_your_api_key_here'
});
// Create a tenant with subdomain
const tenant = await client.createTenant({
name: 'Acme Corporation',
createSubdomain: true,
subdomainName: 'acme',
metadata: {
plan: 'premium',
industry: 'technology'
}
});
// Add a custom domain
const domain = await client.createDomain({
customDomain: 'app.acme.com',
subdomain: 'acme',
tenantId: tenant.data.id,
verificationMethod: 'cname'
});
console.log('Setup complete!', { tenant, domain });
import { DomainlyClient } from 'domainly-sdk';
const client = new DomainlyClient({
apiKey: 'dom_your_api_key_here',
// Optional configuration
baseURL: 'https://api.domainly.dev/external/api', // Default
timeout: 30000, // Default: 30 seconds
retries: 3, // Default: 3 retries
retryDelay: 1000, // Default: 1 second
});
// Set environment variable
process.env.DOMAINLY_API_KEY = 'dom_your_api_key_here';
const client = new DomainlyClient({
apiKey: process.env.DOMAINLY_API_KEY!
});
const tenant = await client.createTenant({
name: 'Customer Company',
createSubdomain: true, // Optional
subdomainName: 'customer', // Optional
metadata: { // Optional
plan: 'enterprise',
industry: 'finance'
}
});
const tenants = await client.getTenants();
console.log(tenants.data); // Array of tenants
const tenant = await client.getTenant('tenant-id');
console.log(tenant.data);
const updatedTenant = await client.updateTenant('tenant-id', {
name: 'Updated Company Name',
status: 'ACTIVE',
metadata: { plan: 'premium' }
});
await client.deleteTenant('tenant-id');
const domain = await client.createDomain({
customDomain: 'app.customer.com',
subdomain: 'customer-subdomain', // Optional
tenantId: 'tenant-id', // Optional
verificationMethod: 'cname', // 'cname' | 'txt' | 'http'
autoRenewSSL: true // Optional
});
const verification = await client.verifyDomain('domain-id');
console.log('Verified:', verification.data.verified);
const records = await client.getCNAMERecords('domain-id');
console.log('CNAME records:', records.data);
const domains = await client.getDomains();
console.log(domains.data);
const subdomain = await client.createSubdomain({
subdomain: 'store',
tenantId: 'tenant-id', // Optional
description: 'E-commerce store subdomain'
});
const availability = await client.checkSubdomainAvailability('desired-subdomain');
console.log('Available:', availability.data.available);
await client.configureWebhook({
webhookUrl: 'https://your-app.com/webhooks/domainly',
secret: 'your-webhook-secret',
events: [
'tenant.created',
'domain.verified',
'ssl.provisioned'
]
});
const config = await client.getWebhookConfig();
console.log(config.data);
const testResult = await client.testWebhook({
webhookUrl: 'https://your-app.com/webhooks/domainly'
});
console.log('Webhook test:', testResult.data.success);
const logs = await client.getWebhookLogs({ limit: 20, offset: 0 });
console.log('Recent webhook deliveries:', logs.data.logs);
console.log('Total events:', logs.data.total);
const result = await client.retryWebhook('webhook-event-id');
console.log('Retry successful:', result.success);
The SDK includes utility functions for common operations:
import { DomainlyUtils } from 'domainly-sdk';
const utils = new DomainlyUtils(client);
// Complete setup with tenant, subdomain, and domain
const setup = await utils.createCompleteSetup({
tenantName: 'Acme Corporation',
subdomainName: 'acme',
customDomain: 'app.acme.com',
verificationMethod: 'cname',
metadata: { plan: 'enterprise' }
});
console.log('Setup result:', setup);
// Wait for domain to be verified with polling
const verifiedDomain = await utils.waitForDomainVerification('domain-id', {
maxAttempts: 30,
pollInterval: 5000, // 5 seconds
timeout: 300000 // 5 minutes
});
console.log('Domain verified!', verifiedDomain);
// Create multiple tenants at once
const batchResult = await utils.batchCreateTenants([
{ name: 'Company A', createSubdomain: true, subdomainName: 'company-a' },
{ name: 'Company B', createSubdomain: true, subdomainName: 'company-b' },
{ name: 'Company C', createSubdomain: true, subdomainName: 'company-c' }
], {
concurrency: 3,
continueOnError: true
});
console.log(`Created ${batchResult.successful} tenants`);
import { DomainUtils, SubdomainUtils, ValidationUtils } from 'domainly-sdk';
// Validate domain format
const domainValidation = DomainUtils.validateDomain('app.example.com');
if (!domainValidation.valid) {
console.error('Invalid domain:', domainValidation.error);
}
// Validate subdomain format
const subdomainValidation = SubdomainUtils.validateSubdomain('my-app');
if (!subdomainValidation.valid) {
console.error('Invalid subdomain:', subdomainValidation.error);
}
// Validate complete requests
const tenantRequestValidation = ValidationUtils.validateCreateTenantRequest({
name: 'Test Company',
subdomainName: 'test-company'
});
if (!tenantRequestValidation.valid) {
console.error('Validation errors:', tenantRequestValidation.errors);
}
import { SubdomainUtils, DomainUtils, ErrorUtils } from 'domainly-sdk';
// Generate slug from company name
const slug = SubdomainUtils.generateSlug('My Company Inc!');
console.log(slug); // 'my-company-inc'
// Check domain status
const isVerified = DomainUtils.isDomainVerified(domain);
const hasSSL = DomainUtils.isSSLActive(domain);
// Get verification instructions
const instructions = DomainUtils.getVerificationInstructions(domain);
console.log(instructions);
// Handle errors gracefully
if (ErrorUtils.isRetryableError(error)) {
// Implement retry logic
}
The SDK provides comprehensive error handling:
import { DomainlyClient, ErrorUtils } from 'domainly-sdk';
try {
const tenant = await client.createTenant({
name: 'Test Company'
});
} catch (error) {
if (error.name === 'DomainlyError') {
console.error('API Error:', {
message: error.message,
status: error.status,
code: error.code,
details: error.details
});
// Check if error is retryable
if (ErrorUtils.isRetryableError(error)) {
console.log('This error can be retried');
}
// Get user-friendly error message
const readableError = ErrorUtils.getReadableError(error);
console.log('User message:', readableError);
} else {
console.error('Unexpected error:', error);
}
}
Configure webhooks to receive real-time notifications:
tenant.created
, tenant.updated
, tenant.deleted
subdomain.created
, subdomain.updated
, subdomain.deleted
domain.created
, domain.updated
, domain.deleted
, domain.verified
, domain.failed
ssl.provisioned
, ssl.expired
, ssl.renewed
{
"event": "domain.verified",
"timestamp": "2024-01-15T12:00:00Z",
"projectId": "project-789",
"domainId": "domain-123",
"data": {
"customDomain": "app.customer.com",
"status": "verified",
"message": "Domain verification completed successfully",
"timestamp": "2024-01-15T12:00:00Z"
}
}
For webhook tests, the payload will include:
{
"event": "webhook.test",
"projectId": "your-project-id",
"domainId": "test-domain-id",
"data": {
"customDomain": "test.example.com",
"status": "verified",
"message": "This is a test webhook from Domainly",
"timestamp": "2024-01-15T12:00:00Z"
},
"timestamp": "2024-01-15T12:00:00Z"
}
import { DomainlyClient, WebhookUtils } from 'domainly-sdk';
class WebhookMonitor {
private client: DomainlyClient;
constructor(apiKey: string) {
this.client = new DomainlyClient({ apiKey });
}
async checkWebhookHealth() {
try {
// Test webhook connectivity
const testResult = await this.client.testWebhook({
webhookUrl: 'https://your-app.com/webhooks/domainly'
});
if (testResult.data.success) {
console.log('✅ Webhook endpoint is healthy');
}
} catch (error) {
console.error('❌ Webhook test failed:', error.message);
}
}
async getFailedWebhooks() {
const logs = await this.client.getWebhookLogs({ limit: 100 });
const failedEvents = logs.data.logs.filter(log => log.status === 'FAILED');
console.log(`Found ${failedEvents.length} failed webhook deliveries`);
return failedEvents;
}
async retryFailedWebhooks() {
const failedEvents = await this.getFailedWebhooks();
for (const event of failedEvents) {
try {
await this.client.retryWebhook(event.id);
console.log(`✅ Retried webhook ${event.id}`);
} catch (error) {
console.error(`❌ Failed to retry webhook ${event.id}:`, error.message);
}
}
}
}
// Usage
const monitor = new WebhookMonitor('dom_your_api_key');
await monitor.checkWebhookHealth();
await monitor.retryFailedWebhooks();
Verify webhook signatures using the built-in utility:
import { WebhookUtils } from 'domainly-sdk';
// In your webhook endpoint
app.post('/webhooks/domainly', express.raw({ type: 'application/json' }), (req, res) => {
const payload = req.body.toString();
const signature = req.headers['x-webhook-signature'];
const secret = process.env.DOMAINLY_WEBHOOK_SECRET;
// Verify the webhook signature
if (!WebhookUtils.verifySignature(payload, signature, secret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Parse the webhook payload
const webhookData = WebhookUtils.parsePayload(payload);
if (!webhookData) {
return res.status(400).json({ error: 'Invalid payload' });
}
// Process the webhook
console.log('Received webhook:', webhookData.event);
res.json({ success: true });
});
import { DomainlyClient, DomainlyUtils } from 'domainly-sdk';
class SaaSIntegration {
private domainly: DomainlyClient;
private utils: DomainlyUtils;
constructor(apiKey: string) {
this.domainly = new DomainlyClient({ apiKey });
this.utils = new DomainlyUtils(this.domainly);
}
async onboardCustomer(customerData: {
name: string;
subdomain: string;
customDomain?: string;
}) {
try {
// Create tenant with subdomain
const tenant = await this.domainly.createTenant({
name: customerData.name,
createSubdomain: true,
subdomainName: customerData.subdomain,
metadata: {
onboardedAt: new Date().toISOString(),
plan: 'starter'
}
});
let domain = null;
// Add custom domain if provided
if (customerData.customDomain) {
domain = await this.domainly.createDomain({
customDomain: customerData.customDomain,
subdomain: customerData.subdomain,
tenantId: tenant.data.id,
verificationMethod: 'cname'
});
// Wait for verification
const verifiedDomain = await this.utils.waitForDomainVerification(
domain.data.id,
{ timeout: 300000 }
);
console.log('Domain verified:', verifiedDomain.customDomain);
}
return {
success: true,
tenant: tenant.data,
domain: domain?.data,
accessUrl: domain?.data?.customDomain || `${customerData.subdomain}.your-app.com`
};
} catch (error) {
console.error('Customer onboarding failed:', error);
return { success: false, error: error.message };
}
}
async getCustomerSummary() {
const summary = await this.utils.getResourcesSummary();
return summary;
}
}
// Usage
const saas = new SaaSIntegration('dom_your_api_key');
const result = await saas.onboardCustomer({
name: 'Acme Corporation',
subdomain: 'acme',
customDomain: 'app.acme.com'
});
console.log('Onboarding result:', result);
async function setupCustomDomain(customDomain: string, subdomain: string) {
const client = new DomainlyClient({
apiKey: process.env.DOMAINLY_API_KEY!
});
// 1. Create domain
const domain = await client.createDomain({
customDomain,
subdomain,
verificationMethod: 'cname'
});
// 2. Get verification records
const records = await client.getCNAMERecords(domain.data.id);
console.log('Add these DNS records:');
records.data.forEach(record => {
console.log(`${record.name} CNAME ${record.value}`);
});
// 3. Poll for verification
console.log('Waiting for DNS propagation...');
const utils = new DomainlyUtils(client);
try {
const verifiedDomain = await utils.waitForDomainVerification(domain.data.id, {
maxAttempts: 60, // 5 minutes with 5-second intervals
pollInterval: 5000
});
console.log('✅ Domain verified successfully!');
console.log('🔒 SSL certificate will be provisioned automatically');
return verifiedDomain;
} catch (error) {
console.error('❌ Domain verification failed:', error.message);
throw error;
}
}
The SDK automatically handles rate limiting with exponential backoff:
Rate limit exceeded responses (429) are automatically retried.
We welcome contributions! Please see our Contributing Guide for details.
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ by the Domainly Team
FAQs
Official TypeScript SDK for Domainly External SaaS API - Manage multi-tenant custom domains with ease
We found that domainly-sdk demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Socket uncovers malicious Rust crates impersonating fast_log to steal Solana and Ethereum wallet keys from source code.
Research
A malicious package uses a QR code as steganography in an innovative technique.
Research
/Security News
Socket identified 80 fake candidates targeting engineering roles, including suspected North Korean operators, exposing the new reality of hiring as a security function.