Misar Docs
MisarMailMisar.BlogMisarReachMisarPostMisar.DevMisar PlatformMisar IdentityMisar Posts API
Contacts

TypeScript SDK

Use @misar/contacts to call the Contacts API from any Misar product server.

Installation

The SDK is a workspace package — no npm install needed inside the monorepo.

{
  "dependencies": {
    "@misar/contacts": "workspace:*"
  }
}

Never import @misar/contacts inside non-misar-io repos (MisarMail, MisarReach, etc.). Use the REST API directly from those products — the service key and base URL are sufficient.

Setup

import { createContactsClient } from "@misar/contacts";

const contacts = createContactsClient({
  baseUrl: "https://api.misar.io/io/contacts",
  serviceKey: process.env.AUDIENCE_SERVICE_KEY,
});

Types

import type { Contact, ContactInput, SocialProfiles, ContactStatus } from "@misar/contacts";

// Social network handles / URLs
interface SocialProfiles {
  linkedin?:  string | null;
  twitter?:   string | null;
  instagram?: string | null;
  telegram?:  string | null;
  whatsapp?:  string | null;
  reddit?:    string | null;
  youtube?:   string | null;
}

// Input shape for create / upsert / update
interface ContactInput {
  email:          string;          // required for create
  firstName?:     string | null;   // max 100 chars
  lastName?:      string | null;   // max 100 chars
  phone?:         string | null;   // max 50 chars
  company?:       string | null;   // max 150 chars
  jobTitle?:      string | null;   // max 100 chars
  socialProfiles?: SocialProfiles;
  status?:        ContactStatus;   // "subscribed" | "unsubscribed" | "bounced" | "complained"
  customFields?:  Record<string, unknown>;  // max 20 keys, 8 KB
  source?:        string | null;   // max 100 chars
  workspaceId?:   string | null;
}

Contact methods

// List (paginated)
const page = await contacts.listContacts(userId, { limit: 50, status: "subscribed" });

// Get single
const contact = await contacts.getContact(userId, contactId);

// Create with full profile
const created = await contacts.createContact(userId, {
  email: "[email protected]",
  firstName: "Alice",
  lastName: "Brown",
  phone: "+1 555 000 9876",
  company: "Acme Corp",
  jobTitle: "Engineer",
  socialProfiles: {
    linkedin: "https://linkedin.com/in/alicebrown",
    twitter: "@alice",
  },
});

// Upsert on email collision (updates existing contact if email matches)
const upserted = await contacts.upsertContact(userId, {
  email: "[email protected]",
  company: "New Company",
  socialProfiles: { telegram: "@alice_tg" },
});

// Update specific fields
const updated = await contacts.updateContact(userId, contactId, {
  phone: "+44 20 7946 0958",
  jobTitle: "Senior Engineer",
  socialProfiles: { linkedin: "https://linkedin.com/in/alice-new" },
  status: "unsubscribed",
});

// Delete
await contacts.deleteContact(userId, contactId);

// Bulk action
const result = await contacts.bulkContacts(userId, "unsubscribe", [id1, id2]);

// Import (up to 5000 rows — supports profile fields)
const imported = await contacts.importContacts(userId, [
  { email: "[email protected]", company: "ACME", jobTitle: "Dev" },
  { email: "[email protected]", source: "csv", phone: "+1 555 111 2222" },
]);

// Stats
const stats = await contacts.getContactStats(userId);
// → { total: 1240, subscribed: 980, unsubscribed: 210, bounced: 38, complained: 12 }

Segment methods

const segments = await contacts.listSegments(userId);
const segment  = await contacts.getSegment(userId, segmentId);
const count    = await contacts.refreshSegment(userId, segmentId);
const preview  = await contacts.previewSegment(userId, {
  operator: "AND",
  rules: [{ field: "status", operator: "eq", value: "subscribed" }],
});

Lead scoring

const rules = await contacts.listScoringRules(userId);

Error handling

import { ContactsError } from "@misar/contacts";

try {
  await contacts.getContact(userId, "bad-id");
} catch (err) {
  if (err instanceof ContactsError) {
    console.error(err.status, err.message);
    // err.status === 429 → rate limited; check Retry-After header
  }
}

Rate limits

Write endpoints (create, upsert, update, delete, bulk, import) are limited to 200 requests per user per 60 seconds. On breach, the API returns 429 Too Many Requests with a Retry-After: <seconds> header. ContactsError.status will be 429.