npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

virtuous-ts

v1.0.1

Published

Type-safe TypeScript client for the Virtuous CRM API: contacts, gifts, campaigns, projects, events, grants, volunteering, tasks, webhooks, query builders, and import/transaction endpoints.

Downloads

318

Readme

virtuous-ts

npm version License: MIT

TypeScript client for the Virtuous CRM API. Methods, request bodies, and response types follow the published Postman collection at docs.virtuoussoftware.com. JSDoc @see links point at the matching collection request.

Requires axios. Query builders also use zod for input checks.

Install

npm install virtuous-ts

Quick start

import { VirtuousClient } from 'virtuous-ts';

const virtuous = new VirtuousClient({
  baseURL: 'https://api.virtuoussoftware.com/api/',
  apiKey: process.env.VIRTUOUS_API_KEY!,
  timeout: 30_000,
});

Use an API key from Virtuous Settings → Connectivity → API Keys. Send it as Authorization: Bearer <key>. OAuth password tokens are available via virtuous.account.getToken when a user-scoped token is required.

Hourly rate limit: 1,500 requests. Response headers include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset.

Create gifts and contacts

Virtuous recommends the transaction endpoints. They match contacts, validate designations, and queue work for the nightly import. Direct POST /Gift and POST /Contact skip that matching and can create duplicates.

await virtuous.gift.createGiftTransaction({
  transactionSource: 'Website',
  contact: {
    email: '[email protected]',
    firstname: 'Sarah',
    lastname: 'Smith',
  },
  giftType: 'Cash',
  amount: '100.00',
  giftDate: '2025-04-05',
  frequency: 'OneTime',
});

await virtuous.contact.createContactTransaction({
  referenceSource: 'Website',
  referenceId: 'donor-1001',
  email: '[email protected]',
  firstName: 'Sarah',
  lastName: 'Smith',
});

Transaction and batch calls return an empty acknowledgement. Records are not created in real time. Subscribe to webhooks if you need create/update notifications.

For many gifts at once, use virtuous.gift.createGiftTransactions. For many contacts, use virtuous.contact.createContactImport.

Query helpers

build*Query helpers turn simple filters into the groups / sortBy / descending body Virtuous Query endpoints expect. Pagination is the query method’s second argument ({ skip, take }, max take 1,000).

import { buildGiftQuery } from 'virtuous-ts';

const query = buildGiftQuery({
  giftDateYear: 2025,
  amountMin: 1000,
  sortBy: 'Gift Date',
  descending: true,
});

const results = await virtuous.gift.queryGiftsWithAbbreviatedDetails(query, {
  skip: 0,
  take: 50,
});

Date filters accept YYYY-MM-DD or M/D/YYYY.

Safe updates

Most PUT endpoints require a full object. Omitting a property clears it. Use the matching update*Safely method to GET the current record, merge your changes, then PUT.

Covered: gift, recurring gift, planned gift, contact, contact address, individual, contact method, contact note, event, event attendee, grant, segment, project, project expense, project note, premium, pledge, gift ask, webhook, volunteer opportunity, volunteer attendance.

await virtuous.gift.updateGiftSafely(giftId, {
  amount: 150,
  notes: 'Amount corrected',
});

Bulk PATCH endpoints (gift.bulkUpdateGifts, project.bulkUpdateProjects) are the opposite: send only the fields you are changing, including each record id.

Clients

| Property | Coverage | |----------|----------| | account | Token, organizations, permissions | | contact | Contacts, query, receipts, collections, import/transaction | | contactIndividual | Individuals, query, avatars, collections | | contactAddresses | Address CRUD, archive | | contactMethod | Phone/email methods | | contactNote | Notes, query, email-to-note | | contactTag | Tag catalog and applications | | contactReference | External reference ids | | organizationGroup | Groups and membership | | relationship | Contact relationships | | tribute | Tribute search and write | | gift | Gifts, transactions, bulk patch, designations query | | giftAsk | Gift asks | | recurringGift | Recurring gifts and payments | | plannedGift | Planned gifts | | pledge | Pledges (/api/v2/Pledge) | | premium | Premiums and inventory | | project | Projects, expenses, notes, roles | | campaign | Campaigns | | communication | Communications | | segment | Segments | | event | Events | | eventAttendee | Event attendees | | eventContact | Event contacts | | grant | Grants | | volunteerOpportunity | Volunteer opportunities | | volunteer | Volunteers, attendance, organizers | | email / emailList | Saved emails and list membership | | search | Global search | | task | Tasks | | webhook | Webhook subscriptions |

Tag and email-list definitions are created in the CRM UI, not through the API. addEmailsToTag and addEmailsToEmailList apply an existing tag or list to addresses already in Virtuous.

GET /Contact/ByReference/{id} and GET /Gift/ByReference/{id} are HMAC-only. API keys may receive 403.

Deprecated Reminder endpoints are not wrapped.

Errors

API failures are thrown as VirtuousApiError:

import { VirtuousApiError } from 'virtuous-ts';

try {
  await virtuous.gift.getGift(99999);
} catch (error) {
  if (error instanceof VirtuousApiError) {
    console.error(error.message, error.status, error.code);
  }
}

License

MIT. See LICENSE.