Skip to main content
This recipe walks through building an integration between an auction or event-management platform and Virtuous CRM+. Event and auction platforms have data-modeling demands that straight payment integrations don’t: a single transaction may include a ticket purchase (not tax-deductible), a donation portion (tax-deductible), a raffle entry, and an auction item win — each of which needs to be recorded differently in Virtuous. Sponsorships at varying tiers and in-kind gifts of auction inventory add further complexity. The recipe is platform-agnostic. It applies to event registration platforms (Eventbrite-style ticketing for nonprofit galas), full auction platforms (silent auctions, live auctions, mobile bidding), and combined event+auction platforms (the typical nonprofit gala). The Virtuous-side modeling is the same; the source-platform specifics differ.
This recipe describes patterns that apply across the event and auction platform category. Specific platforms (Greater Giving, OneCause, BiddingForGood, Handbid, Givebutter, and others) have their own data models — confirm against the specific platform’s current documentation when implementing. The Virtuous-side mapping is the durable part.

What’s distinct about event and auction sync

ConcernStandard donation syncEvent/auction sync
Transaction compositionOne transaction = one donationOne transaction may include ticket + donation + auction item + raffle ticket, each priced and tax-treated differently
Tax-deductible portionAlways equals the full giftOften less than the full transaction — the fair-market value of attendance/items is subtracted
Gift type varietyMostly Cash/Credit/EFTAdds NonCash (auction items received), with mix of cash and non-cash within one event
Event linkageOptional metadataCentral — every transaction is tied to the specific event
Refunds and cancellationsRareCommon — donors cancel ticket purchases, auction wins can be voided
Each of these affects how the integration models data on the Virtuous side. The patterns below walk through each.

Architecture

The architecturally distinct component: the order splitter. Where a Stripe charge maps one-to-one to a Virtuous Gift, an auction-platform order may decompose into three, four, or more Virtuous records. The splitter is the logic that turns a single platform order into the right set of Virtuous submissions.

Decomposing an order

A typical gala order from a customer’s auction platform might look like this:
LineDescriptionAmountTreatment
12× Gala tickets at $250 each$500.00Ticket — split fair-market value vs. deductible portion
2Live auction win: “Weekend in Napa”$1,200.00NonCash item; deductible = winning bid − fair-market value
3Silent auction win: “Coffee gift basket”$80.00NonCash item; deductible = winning bid − fair-market value
4Cash donation$500.00Standard cash gift; fully deductible
5Raffle ticket$20.00Not a gift — not tax-deductible; usually recorded as a separate gift type or skipped
Total charged$2,300.00
This one order needs to produce four or more Virtuous records: one or two Gifts for the tickets (or one combined ticket gift), two Gifts for the auction items, one Gift for the cash donation, and either a non-gift transaction record or a skipped line for the raffle ticket.

The splitter pattern

JavaScript
async function splitOrderIntoGifts(order) {
  const submissions = [];
  const orderContactId = order.attendee.platformId;

  for (const line of order.lines) {
    switch (line.type) {
      case 'ticket':
        submissions.push(buildTicketGift(order, line, orderContactId));
        break;
      case 'auction_item':
        submissions.push(buildAuctionItemGift(order, line, orderContactId));
        break;
      case 'cash_donation':
        submissions.push(buildCashGift(order, line, orderContactId));
        break;
      case 'raffle':
        // Most customers either skip raffle lines or record them as a separate
        // gift type with a clear designation. Confirm with the customer.
        if (config.recordRaffles) {
          submissions.push(buildRaffleGift(order, line, orderContactId));
        }
        break;
      case 'sponsorship':
        submissions.push(buildSponsorshipGift(order, line, orderContactId));
        break;
    }
  }

  return submissions;
}
Each line type has a builder function that returns a properly-formed Gift Transaction submission. The next sections cover the builders.

Pattern 1: tickets

A ticket purchase is partially a gift and partially a fair-market-value exchange (the attendee receives dinner, entertainment, etc.). The tax-deductible portion is the ticket price minus the fair-market value of what the attendee receives. The Virtuous data model handles this through giftPremiums — items the donor received in exchange for their gift, with associated fair-market value.
JavaScript
function buildTicketGift(order, line, contactId) {
  const ticketPriceTotal = line.unitPrice * line.quantity;
  const fmvPerTicket = order.event.fairMarketValuePerTicket;
  const fmvTotal = fmvPerTicket * line.quantity;
  const premiumId = order.event.ticketPremiumId; // configured at integration setup

  return {
    transactionSource: 'EventPlatform',
    transactionId: `${order.id}-ticket-${line.id}`,
    contact: { referenceId: contactId, /* ... */ },
    giftDate: order.processedDate,
    giftType: 'Credit',
    amount: ticketPriceTotal,
    giftDesignations: [
      {
        projectCode: mapEventToProjectCode(order.event),
        amountDesignated: ticketPriceTotal,
      },
    ],
    giftPremiums: [
      {
        premiumId,                              // Virtuous Premium ID for "Gala Ticket"
        quantity: line.quantity,
        valuePerItem: fmvPerTicket,
      },
    ],
    customFields: [
      { name: 'Event', value: order.event.name },
      { name: 'Number of Attendees', value: line.quantity.toString() },
    ],
  };
}
Virtuous calculates the tax-deductible portion automatically: deductible = amount - sum(premiums[].quantity × valuePerItem). For two 250ticketswhereeachticketcarries250 tickets where each ticket carries 50 of fair-market value, the deductible portion is 400ofthe400 of the 500 charged.
The Premium records must be configured in Virtuous before the integration can reference them. Customers set up “Gala Ticket,” “Auction Item,” “VIP Reception” premium records with their associated fair-market values, then your integration references the resulting premiumId. Walk through Premium configuration with the customer’s team at integration setup. The GET /api/Premium/Query endpoint (where available) lets your integration discover configured premiums programmatically.

Splitting one order across multiple attendees

If the ticket purchaser bought tickets for guests (different attendees), the purchaser is the donor of record but each attendee may also need a Contact record for event-day check-in, allergies, dietary restrictions. This is event-platform-specific — most platforms capture per-ticket attendee data. Submit Contact Transactions for each attendee separately, then use a custom field or note on the Gift to link them.

Pattern 2: auction items

An auction-item purchase has the same structure as a ticket — partially deductible, with the non-deductible portion being the item’s fair-market value. Use the same giftPremiums pattern:
JavaScript
function buildAuctionItemGift(order, line, contactId) {
  const winningBid = line.amount;
  const itemFMV = line.itemFairMarketValue;
  const premiumId = line.itemPremiumId;       // each auction item is a Premium

  return {
    transactionSource: 'EventPlatform',
    transactionId: `${order.id}-auction-${line.id}`,
    contact: { referenceId: contactId, /* ... */ },
    giftDate: order.processedDate,
    giftType: 'Credit',
    amount: winningBid,
    giftDesignations: [
      {
        projectCode: mapEventToProjectCode(order.event),
        amountDesignated: winningBid,
      },
    ],
    giftPremiums: [
      {
        premiumId,
        quantity: 1,
        valuePerItem: itemFMV,
      },
    ],
    customFields: [
      { name: 'Auction Item', value: line.itemName },
      { name: 'Item Fair Market Value', value: itemFMV.toFixed(2) },
    ],
  };
}
The deductible portion is winningBid - itemFMV. A 1,200winningbidonanitemworth1,200 winning bid on an item worth 400 produces an $800 deductible gift.

Recording donated auction items as NonCash gifts

When a donor contributes an auction item (a vacation, a piece of art, an experience) for the auction to sell, that donation itself is a NonCash gift from the item donor to the nonprofit:
JavaScript
async function recordDonatedAuctionItem(item) {
  return submitGiftTransaction({
    transactionSource: 'EventPlatform',
    transactionId: `donated-item-${item.id}`,
    contact: { referenceId: item.donor.platformId, /* ... */ },
    giftDate: item.donationDate,
    giftType: 'NonCash',
    nonCashGiftType: 'Auction Item',          // discover via /api/Gift/NonCashGiftTypes
    inKindDescription: item.description,
    inKindValue: item.fairMarketValue,
    amount: item.fairMarketValue,             // amount equals FMV for NonCash gifts
    giftDesignations: [
      {
        projectCode: mapEventToProjectCode(item.event),
        amountDesignated: item.fairMarketValue,
      },
    ],
  });
}
This is a separate Gift from the auction-win Gift. The donor of the item and the winning bidder are typically different people; both have records of their giving to the event.

Pattern 3: cash donations within an event

A pure cash donation (no fair-market value received) follows the standard pattern — see Create a Donation:
JavaScript
function buildCashGift(order, line, contactId) {
  return {
    transactionSource: 'EventPlatform',
    transactionId: `${order.id}-donation-${line.id}`,
    contact: { referenceId: contactId, /* ... */ },
    giftDate: order.processedDate,
    giftType: 'Credit',
    amount: line.amount,
    giftDesignations: [
      {
        projectCode: mapEventToProjectCode(order.event),
        amountDesignated: line.amount,
      },
    ],
    customFields: [
      { name: 'Event', value: order.event.name },
    ],
  };
}
No giftPremiums — the donor received nothing in exchange, so the full amount is deductible.

Pattern 4: sponsorships

Corporate or individual sponsorships at named tiers (Gold Sponsor, Silver Sponsor, etc.) are a hybrid: part marketing-value (the sponsor’s logo on materials, named recognition during the event) and part charitable gift. Treat them as Gifts with Premiums representing the sponsorship benefits:
JavaScript
function buildSponsorshipGift(order, line, contactId) {
  const sponsorshipTier = order.event.sponsorshipTiers[line.tier];

  return {
    transactionSource: 'EventPlatform',
    transactionId: `${order.id}-sponsorship-${line.id}`,
    contact: { referenceId: contactId, /* ... */ },
    giftDate: order.processedDate,
    giftType: 'Credit',
    amount: line.amount,
    giftDesignations: [
      {
        projectCode: mapEventToProjectCode(order.event),
        amountDesignated: line.amount,
      },
    ],
    giftPremiums: [
      {
        premiumId: sponsorshipTier.premiumId,
        quantity: 1,
        valuePerItem: sponsorshipTier.fairMarketValue,  // value of the benefits package
      },
    ],
    customFields: [
      { name: 'Sponsorship Tier', value: line.tier },
      { name: 'Event', value: order.event.name },
    ],
    tags: 'Sponsor',                          // tag the donor as a sponsor
  };
}
If the sponsor is an Organization Contact (typical for corporate sponsorships), make sure the Contact Transaction creates them as contactType: "Organization", not as a Household.

Refunds and cancellations

Event and auction transactions are refunded more often than straight donations — attendees cancel before the event, auction wins are voided when the winner doesn’t pay or returns the item. Use the same reversing-transaction pattern from Stripe to Virtuous:
JavaScript
async function refundGift(originalGiftId, refundDate, notes) {
  return fetch('https://api.virtuoussoftware.com/api/Gift/ReversingTransaction', {
    method: 'POST',
    headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      reversedGiftId: originalGiftId,
      giftDate: refundDate,
      notes: notes ?? 'Event cancellation refund',
    }),
  });
}
When an entire order is cancelled, reverse each of the Gifts the splitter originally created. Each line item’s reversing transaction is its own record — don’t try to consolidate them.

Partial refunds

If only one line of a multi-line order is refunded (e.g., the attendee cancelled the auction-item win but kept the tickets and the cash donation), reverse only the affected Gift. The other Gifts remain intact.

Event-day registration vs. pre-event purchase

Most event platforms support both pre-event ticket purchases (online, weeks ahead) and day-of registration (walk-up at the venue). Both flow through your integration with the same patterns, but day-of registrations often arrive in batches at the end of the event when the platform reconciles its register. If your customer’s event platform delivers day-of registrations as a batch (rather than per-attendee in real-time), the inbound webhook receiver may need to handle high-burst volume. The standard queue-based architecture handles this naturally — the queue absorbs the burst and the submitter drains at the throttled rate.

Common edge cases

A donor gives but doesn’t attend

Sometimes a supporter buys tickets but can’t attend, and explicitly donates the ticket value back to the nonprofit. The order has tickets but no attendees. Two patterns:
  • Treat the unused tickets as a donation. Submit a cash Gift for the ticket value with no giftPremiums. The donor’s full payment is deductible because they received no benefit.
  • Maintain the ticket Gift with a “Unused” tag. Keeps the data symmetric with attended tickets. Less accurate for tax reporting.
Most customers prefer the first pattern. Detect “ticket purchased but not attended” via the platform’s attendance data, and at end-of-event reconcile by adjusting the Gift.

A donor with no email

Walk-up attendees who pay cash at the door may not provide an email or any identifier — just a name. The Contact Transaction matching algorithm needs at least some signal to avoid creating endless duplicate “John Smith” records. Two patterns:
  • Require some identifier. Decline to sync attendees with no email, no phone, and no address.
  • Tag and review. Sync as a new Contact with a “Walk-up — Needs Review” tag for the customer’s team to merge against existing records manually.

Multiple attendees per ticket purchase

When the ticket purchaser brought guests, each attendee is a Contact but only the purchaser is the donor on the financial gift. Capture guest Contacts via a separate sync path (often through a check-in app’s data, not the order itself) and link them to the event via the Virtuous Event record.

Pledged donations during a paddle raise

Live-event paddle raises produce pledges (verbal commitments at the event) that get charged later. Treat these as Pledges in Virtuous (/api/v2/Pledge/* endpoints) at the time of the raise, then as Gift payments against the pledge when the charge actually processes.

Production readiness checklist

  • Each line of a multi-line order produces its own Virtuous Gift submission.
  • Tickets use giftPremiums to capture the fair-market value of attendance.
  • Auction-item winning bids use giftPremiums with the item’s FMV.
  • Donated auction items (in-kind gifts) are recorded as separate NonCash Gifts from the item donor.
  • Sponsorships at named tiers are linked to the corresponding Premium records.
  • Corporate sponsors are created as Organization-type Contacts.
  • Premium records are configured in Virtuous before the integration references them.
  • Refunds use POST /api/Gift/ReversingTransaction per-line, not order-level deletion.
  • Raffle and non-deductible line items are either skipped or recorded with a clear non-deductible designation.
  • Walk-up / no-email attendees are flagged for customer review rather than silently creating duplicates.
  • Reconciliation queries match the event’s total revenue against the sum of Virtuous Gifts for the event’s Project.

Where to go next

Fundraising Platform to Virtuous CRM

The companion recipe for peer-to-peer fundraising — relevant when your event includes participant fundraisers.

Stripe to Virtuous CRM

The payment-processor recipe that often powers event platforms underneath.

Create a Donation

The base Gift Transaction workflow, including the NonCash and stock-gift variants used for in-kind items.

Donations / Gifts

The Gift data model including premiums, designations, and gift type fields used throughout this recipe.
Last modified on May 27, 2026