Metadata-Version: 2.4
Name: neoncrm
Version: 0.1.0
Summary: Python SDK for the Neon CRM API v2
Author: Better Than Eleven, LLC
License-Expression: MIT
Project-URL: Homepage, https://github.com/Coldwater-of-Lees-Summit/neoncrm-sdk
Project-URL: Repository, https://github.com/Coldwater-of-Lees-Summit/neoncrm-sdk
Project-URL: Issues, https://github.com/Coldwater-of-Lees-Summit/neoncrm-sdk/issues
Keywords: neoncrm,neon,crm,api,sdk,nonprofit
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.24.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: respx>=0.20; extra == "dev"
Dynamic: license-file

# neoncrm

Python SDK for the [Neon CRM](https://www.neoncrm.com/) API v2.

## Installation

```bash
pip install .
```

## Quick Start

```python
from neoncrm import NeonCRM

neon = NeonCRM(org_id="YOUR_ORG_ID", api_key="YOUR_API_KEY")

# List accounts
accounts = neon.accounts.list(first_name="Jane")

# Get a single account
account = neon.accounts.get("12345")

# Create an individual account
result = neon.accounts.create({
    "individualAccount": {
        "primaryContact": {
            "firstName": "Jane",
            "lastName": "Doe",
            "email1": "jane@example.com",
        }
    }
})

# Search accounts
results = neon.accounts.search({
    "searchFields": [
        {"field": "Email", "operator": "EQUAL", "value": "jane@example.com"}
    ],
    "outputFields": ["Account ID", "First Name", "Last Name", "Email 1"],
    "pagination": {"currentPage": 0, "pageSize": 50},
})
```

## Authentication

The SDK uses HTTP Basic authentication. You need:

- **Org ID** — found in Settings > Organization Profile > Account Information
- **API Key** — found in Settings > User Management > select user > enable API Access

```python
# Production (default)
neon = NeonCRM(org_id="123", api_key="abc...")

# Trial instance
neon = NeonCRM(org_id="123", api_key="abc...", environment="trial")

# Custom base URL
neon = NeonCRM(org_id="123", api_key="abc...", base_url="https://custom.example.com/v2")
```

## Resources

| Resource | Access | Description |
|---|---|---|
| `neon.accounts` | Accounts | Individuals and companies |
| `neon.activities` | Activities | Activity tracking |
| `neon.addresses` | Addresses | Physical addresses |
| `neon.campaigns` | Campaigns | Fundraising campaigns |
| `neon.custom_fields` | Custom Fields | Custom fields and groups |
| `neon.donations` | Donations | Donation records |
| `neon.events` | Events | Event management |
| `neon.event_registrations` | Event Registrations | Registration management |
| `neon.grants` | Grants | Grant records |
| `neon.memberships` | Memberships | Membership management |
| `neon.orders` | Orders | Online store orders |
| `neon.payments` | Payments | Payment processing |
| `neon.pledges` | Pledges | Pledge management |
| `neon.properties` | Properties | System lookup values |
| `neon.recurring_donations` | Recurring Donations | Recurring giving |
| `neon.store` | Online Store | Products and catalogs |
| `neon.webhooks` | Webhooks | Webhook subscriptions |
| `neon.volunteers` | Volunteers | Volunteers, groups, opportunities, roles, shifts, time sheets |
| `neon.households` | Households | Household records |

## Common Patterns

### CRUD Operations

Most resources follow a consistent pattern:

```python
# Create
result = neon.donations.create({"accountId": "123", "amount": 50.00, ...})

# Read
donation = neon.donations.get("456")

# Update (full)
neon.donations.update("456", {... full object ...})

# Update (partial)
neon.donations.patch("456", {"amount": 75.00})

# Delete
neon.donations.delete("456")
```

### Search

Resources with search support provide `search()`, `search_fields()`, and `output_fields()`:

```python
# Discover available search fields
fields = neon.donations.search_fields()

# Discover available output columns
columns = neon.donations.output_fields()

# Run a search
results = neon.donations.search({
    "searchFields": [
        {"field": "Donation Date", "operator": "GREATER_AND_EQUAL", "value": "2024-01-01"}
    ],
    "outputFields": ["Donation ID", "Amount", "Donor Name"],
    "pagination": {"currentPage": 0, "pageSize": 100},
})
```

### Auto-Pagination

Use `list_all()` to iterate through all pages automatically:

```python
for account in neon.accounts.list_all(last_name="Smith"):
    print(account["accountId"])
```

### Events

```python
# List upcoming events
events = neon.events.list(archived=False, start_date_after="2024-06-01T00:00:00Z")

# Get attendees
attendees = neon.events.attendees("789")

# Get event categories
categories = neon.events.categories()
```

### Memberships

```python
# Get membership levels and terms
levels = neon.memberships.levels()
terms = neon.memberships.terms()

# Create a membership
neon.memberships.create({...})

# Renew
neon.memberships.renew("456", {... renewal data ...})
```

### Webhooks

```python
# List existing webhooks
hooks = neon.webhooks.list()

# Create a webhook
neon.webhooks.create({
    "webhookName": "New Donation",
    "url": "https://example.com/hooks/donation",
    "eventTrigger": "createDonation",
})
```

### System Properties

```python
# Lookup values for forms
sources = neon.properties.sources()
funds = neon.properties.funds()
countries = neon.properties.countries()
genders = neon.properties.genders()
```

## Error Handling

```python
from neoncrm import (
    NeonCRMError,
    AuthenticationError,
    NotFoundError,
    ValidationError,
    ServerError,
)

try:
    neon.accounts.get("nonexistent")
except NotFoundError as e:
    print(f"Not found: {e}")
except AuthenticationError:
    print("Check your org_id and api_key")
except ValidationError as e:
    print(f"Bad request: {e.response_body}")
except ServerError:
    print("Neon CRM server error, try again later")
except NeonCRMError as e:
    print(f"API error {e.status_code}: {e}")
```

## Context Manager

```python
with NeonCRM(org_id="123", api_key="abc...") as neon:
    accounts = neon.accounts.list()
```

## API Version

The SDK defaults to API version 2.11. Override if needed:

```python
neon = NeonCRM(org_id="123", api_key="abc...", api_version="2.10")
```

## Development

```bash
pip install -e ".[dev]"
pytest
```
