outseta 2.0.0

SDKdartflutter
Platformandroidioswindowslinuxmacosweb

API client for projects using the Outseta membership operating system.

Outseta Dart API Client

Screenshots

A comprehensive Dart API client for the Outseta.com REST API V1. This library provides a type-safe interface to interact with all Outseta API endpoints including CRM, Billing, Marketing, and Support functions.

🚨 Breaking Changes in v2.0.0

The User model has been renamed to Profile throughout the codebase. If you're upgrading from v1.x, you'll need to:

  • Update import statements from User to Profile
  • Change variable declarations from User to Profile
  • Update method calls that returned User objects to expect Profile objects

pub package License: MIT

The change reflects the official Outseta API more accurately. The refactoring maintains 100% functional compatibility - only the naming has changed. All existing functionality, JSON serialization/deserialization, API endpoints, and behavior remain exactly the same.

Features

  • Authentication: Support for both API key auth (server-side) and bearer token auth (client-side)
  • CRM: Manage people, accounts, and deals
  • Billing: Handle subscriptions, invoices, plans, and payments
  • Marketing: Work with email campaigns, lists, and subscribers
  • Support: Create and manage support tickets
  • User Profile: Manage user profiles and authentication
  • Type-safe: All models are fully typed with JSON serialization support
  • Pagination: Built-in support for paginated responses

Screenshots

API Overview

You can read the accompanying article at DartFoundry.com.

ModuleFunctionalityKey MethodsModels
CRMCustomer relationship managementgetPeople(), createAccount(), getDeal()Person, Account, Deal
BillingSubscription and payment managementgetPlans(), createSubscription(), getInvoice()Plan, Subscription, Invoice, Payment
MarketingEmail campaigns and listsgetLists(), createEmail(), addSubscriber()EmailList, Email
SupportHelp desk and ticketsgetTickets(), addComment(), changeStatus()Ticket
User ProfileUser authentication and profilegetCurrentUser(), updateProfilePicture(), changePassword()Profile

Getting Started

Add the package to your pubspec.yaml:

dependencies:
  outseta: ^2.0.0

Then run:

dart pub get

Usage

Initializing the client

import 'package:outseta/outseta.dart';

// For server-side usage with API keys
final client = OutsetaClient(
  baseUrl: 'https://your-domain.outseta.com/api/v1',
  auth: ApiKeyAuth(
    apiKey: 'your-api-key',
    secretKey: 'your-secret-key',
  ),
);

// For client-side usage with bearer token
final client = OutsetaClient(
  baseUrl: 'https://your-domain.outseta.com/api/v1',
  auth: BearerTokenAuth(
    accessToken: 'user-access-token',
  ),
);

Working with the CRM

// Get people
final peopleResponse = await client.crm.getPeople(limit: 10);
for (final person in peopleResponse.items) {
  print('${person.fullName} (${person.email})');
}

// Get a specific account
final account = await client.crm.getAccount('account-uid');
print(account.name);

// Create a new person
final newPerson = Person(
  firstName: 'John',
  lastName: 'Doe',
  email: 'john.doe@example.com',
);
final createdPerson = await client.crm.createPerson(newPerson);

Working with Billing

// Get available plans
final plansResponse = await client.billing.getPlans();
for (final plan in plansResponse.items) {
  print('${plan.name}: \$${plan.amount} per ${plan.billingTerm?.toLowerCase()}');
}

// Get a specific subscription
final subscription = await client.billing.getSubscription('subscription-uid');
print('Subscription status: ${subscription.status}');

Working with Marketing

// Get email lists
final listsResponse = await client.marketing.getLists(limit: 20);
for (final list in listsResponse.items) {
  print('${list.name}: ${list.subscriberCount} subscribers');
}

// Create a new email list
final newList = EmailList(
  name: 'Newsletter Subscribers',
  description: 'People who want to receive our monthly newsletter',
);
final createdList = await client.marketing.createList(newList);

// Add a subscriber to the list
await client.marketing.addSubscriber(
  createdList.uid!,
  'person-uid-here',
);

// Create and schedule an email campaign
final emailCampaign = Email(
  subject: 'Monthly Newsletter - January',
  fromName: 'Your Company',
  fromEmail: 'newsletter@example.com',
  content: '<h1>January Newsletter</h1><p>Here are our updates...</p>',
  emailListUid: createdList.uid,
);
final createdEmail = await client.marketing.createEmail(emailCampaign);

// Schedule the email to be sent
final tomorrow = DateTime.now().add(Duration(days: 1));
await client.marketing.scheduleEmail(createdEmail.uid!, tomorrow);

Managing Support Tickets

// Create a support ticket
final ticket = Ticket(
  subject: 'Help needed',
  description: 'I need help with my account',
  priority: 'Medium',
);
final createdTicket = await client.support.createTicket(ticket);

// Add a comment to a ticket
await client.support.addComment(
  createdTicket.uid!,
  'This is a comment',
  isPrivate: true,
);

// Change the status of a ticket
await client.support.changeStatus(createdTicket.uid!, 'In Progress');

// Assign a ticket to a support agent
await client.support.assignTicket(createdTicket.uid!, 'support-agent-uid');

// Get tickets assigned to a specific person
final assignedTickets = await client.support.getTicketsAssignedToPerson(
  'support-agent-uid',
  limit: 10,
);
print('Found ${assignedTickets.metadata.total} assigned tickets');

Working with User Profiles

// Get the current user's profile
final currentProfile = await client.userProfile.getCurrentUser();
print('Logged in as: ${currentProfile.email}');

// Update a user's profile
final updatedProfile = currentProfile.copyWith(
  firstName: 'New First Name',
);
await client.userProfile.updateCurrentUser(updatedProfile);

// Change a user's password
await client.userProfile.changePassword(
  'current-password',
  'new-password',
  'new-password',
);

// Request a password reset for a user
await client.userProfile.requestPasswordReset('user@example.com');

// Get an access token for client-side authentication
final token = await client.userProfile.getAccessToken(
  'username@example.com',
  'password',
);
print('Access token: $token');

// Update a profile picture (base64 encoded image)
final base64Image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhE...';
await client.userProfile.updateProfilePicture(base64Image);

Additional Information

Authentication

Outseta supports two authentication methods:

  1. API Key Authentication: For server-side applications. Create API keys in your Outseta account under Settings > Integrations > API Keys.

  2. Bearer Token Authentication: For client-side applications. Get a token by using the getAuthToken function or through the UserProfileApi.

Pagination

All list endpoints return paginated responses with metadata:

final response = await client.crm.getPeople(offset: 0, limit: 10);
print('Total people: ${response.metadata.total}');
print('Current page size: ${response.metadata.count}');
print('Offset: ${response.metadata.offset}');

Error Handling

The client provides typed exceptions for different error scenarios:

try {
  await client.crm.getPerson('invalid-uid');
} on NotFoundException catch (e) {
  print('Person not found: ${e.message}');
} on UnauthorizedException catch (e) {
  print('Authentication error: ${e.message}');
} on ApiException catch (e) {
  print('API error: ${e.message}');
}

API Reference

Billing API

MethodDescriptionParameters
getPlans()Get a paginated list of plansoffset, limit, filter
getPlan()Get a plan by UIDuid
createPlan()Create a new planplan
updatePlan()Update an existing planplan
deletePlan()Delete a planuid
getSubscriptions()Get a paginated list of subscriptionsoffset, limit, filter
getSubscription()Get a subscription by UIDuid
createSubscription()Create a new subscriptionsubscription
updateSubscription()Update an existing subscriptionsubscription
cancelSubscription()Cancel a subscriptionuid, cancellationReason
getInvoices()Get a paginated list of invoicesoffset, limit, filter
getInvoice()Get an invoice by UIDuid
createInvoice()Create a new invoiceinvoice
updateInvoice()Update an existing invoiceinvoice
markInvoiceAsPaid()Mark an invoice as paiduid
getPayments()Get a paginated list of paymentsoffset, limit, filter
getPayment()Get a payment by UIDuid
createPayment()Create a new paymentpayment
refundPayment()Refund a paymentuid, amount

CRM API

MethodDescriptionParameters
getPeople()Get a paginated list of peopleoffset, limit, filter
getPerson()Get a person by UIDuid
createPerson()Create a new personperson
updatePerson()Update an existing personperson
deletePerson()Delete a personuid
getAccounts()Get a paginated list of accountsoffset, limit, filter
getAccount()Get an account by UIDuid
createAccount()Create a new accountaccount
updateAccount()Update an existing accountaccount
deleteAccount()Delete an accountuid
addPersonToAccount()Add a person to an accountaccountUid, personUid
removePersonFromAccount()Remove a person from an accountaccountUid, personUid
getDeals()Get a paginated list of dealsoffset, limit, filter
getDeal()Get a deal by UIDuid
createDeal()Create a new dealdeal
updateDeal()Update an existing dealdeal
deleteDeal()Delete a dealuid

Marketing API

MethodDescriptionParameters
getLists()Get a paginated list of email listsoffset, limit, filter
getList()Get an email list by UIDuid
createList()Create a new email listlist
updateList()Update an existing email listlist
deleteList()Delete an email listuid
getSubscribers()Get subscribers for a listlistUid, offset, limit, filter
addSubscriber()Add a subscriber to a listlistUid, personUid
removeSubscriber()Remove a subscriber from a listlistUid, personUid
getEmails()Get a paginated list of email campaignsoffset, limit, filter
getEmail()Get an email campaign by UIDuid
createEmail()Create a new email campaignemail
updateEmail()Update an existing email campaignemail
deleteEmail()Delete an email campaignuid
sendTestEmail()Send a test emailemailUid, recipientEmail
scheduleEmail()Schedule an email campaignemailUid, scheduledDate
sendEmailNow()Send an email campaign immediatelyemailUid
cancelScheduledEmail()Cancel a scheduled email campaignemailUid

Support API

MethodDescriptionParameters
getTickets()Get a paginated list of ticketsoffset, limit, filter
getTicket()Get a ticket by UIDuid
createTicket()Create a new ticketticket
updateTicket()Update an existing ticketticket
deleteTicket()Delete a ticketuid
addComment()Add a comment to a ticketticketUid, comment, isPrivate
changeStatus()Change the status of a ticketticketUid, status
assignTicket()Assign a ticket to a personticketUid, personUid
getTicketsAssignedToPerson()Get tickets assigned to a personpersonUid, offset, limit
getTicketsSubmittedByPerson()Get tickets submitted by a personpersonUid, offset, limit

User Profile API

MethodDescriptionParameters
getCurrentUser()Get the current user's profilenone
updateCurrentUser()Update the current user's profileprofile
changePassword()Change the current user's passwordcurrentPassword, newPassword, confirmPassword
requestPasswordReset()Request a password reset for a useremail
resetPassword()Reset a user's password using a tokentoken, newPassword, confirmPassword
getAccessToken()Get an access token for client-side authusername, password
verifyEmail()Verify an email addresstoken
requestEmailVerification()Request a new email verification tokenemail
updateProfilePicture()Update the user's profile picturebase64Image

Testing

The package includes comprehensive tests for all API modules:

# Run all tests
dart run test

# Run tests with coverage
dart run test --coverage=coverage

Integration Tests

Some functionality, like the getAuthToken() function, requires actual API credentials and network connections. These tests are separate from the main test suite:

# Create a .env file with your Outseta credentials
# OUTSETA_BASE_URL=https://your-domain.outseta.com/api/v1
# OUTSETA_USERNAME=your-username
# OUTSETA_PASSWORD=your-password

# Run integration tests
dart test --tags=integration integration_test/

Test Coverage

The package includes comprehensive tests with high code coverage for all components:

  • Authentication: API Key and Bearer Token authentication methods
  • Exception Handling: All exception types and error scenarios
  • OutsetaClient: HTTP methods (GET, POST, PUT, DELETE) and error handling
  • CRM API: People, accounts, and deals operations
  • Billing API: Plans, subscriptions, invoices, and payments management
  • Marketing API: Lists, subscribers, and email campaigns
  • Support API: Tickets, comments, and ticket management
  • User Profile API: User profile management and authentication
  • Models: Serialization/deserialization, equality comparison, and copying

Note: Some functions, like getAuthToken(), are excluded from coverage metrics as they require actual API connections. See test/coverage_exclusions/README.md for details on these exclusions.

Each API module has tests for:

  • Retrieving paginated collections
  • Getting individual resources
  • Creating and updating resources
  • Special operations specific to that module
  • Error handling and validation

Viewing Test Coverage

To view the coverage report:

# Install coverage tools if you haven't already
dart pub global activate coverage

# Installing lcov (required for HTML reports)
# On macOS
brew install lcov

# On Ubuntu/Debian
sudo apt-get install lcov

# On Windows (using Chocolatey)
choco install lcov

# Run tests with coverage
dart run test --coverage=coverage

# Process the coverage report (excluding generated files)
dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --report-on=lib/ --exclude-files="lib/src/generated/**,**/*.g.dart"

# Generate HTML report (requires lcov)
genhtml -o coverage/html coverage/lcov.info

# Open the HTML report
open coverage/html/index.html  # On macOS
xdg-open coverage/html/index.html  # On Linux
start coverage/html/index.html  # On Windows

The dart_test.yaml file in the project root includes coverage configurations that exclude functions that can't be properly unit tested, such as those requiring actual network connections.

For VS Code users, you can also use the "Dart Code Coverage" extension to visualize coverage directly in your editor.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Copyright (c) 2025 Dom Jocubeit

License

This project is licensed under the MIT License - see the LICENSE file for details.