PHPackages                             jcolombo/paymo-api-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. jcolombo/paymo-api-php

ActiveLibrary

jcolombo/paymo-api-php
======================

PHP implementation of the Paymo App API

v0.6.1(5mo ago)044MITPHPPHP &gt;=7.4

Since Mar 2Pushed 3w ago2 watchersCompare

[ Source](https://github.com/jcolombo/paymo-api-php)[ Packagist](https://packagist.org/packages/jcolombo/paymo-api-php)[ RSS](/packages/jcolombo-paymo-api-php/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (3)Dependencies (3)Versions (12)Used By (0)

Paymo API for PHP
=================

[](#paymo-api-for-php)

A robust, object-oriented PHP SDK for the [Paymo](https://www.paymoapp.com) project management API.

[![Latest Version](https://camo.githubusercontent.com/2a6456b184ba674a4f92e0cb8fde349d3b7addb4e45f82137c30937f51784aaa/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6a636f6c6f6d626f2f7061796d6f2d6170692d7068702e737667)](https://packagist.org/packages/jcolombo/paymo-api-php)[![PHP Version](https://camo.githubusercontent.com/4f7ebc7de57c6f5fd479a61aa2c34b60a8fede848ade7d1fe3d1dffd4bba4df1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f6a636f6c6f6d626f2f7061796d6f2d6170692d7068702e737667)](https://packagist.org/packages/jcolombo/paymo-api-php)[![License](https://camo.githubusercontent.com/4d7575a7fe5689d35ba789d779057573a53cbdcd94795f4d39101a006d1ba9ef/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6a636f6c6f6d626f2f7061796d6f2d6170692d706870)](LICENSE)[![GitHub Issues](https://camo.githubusercontent.com/7406395cf06fa44e9388dc6513eea12829c229cd37606aab2137ebab731998a2/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732f6a636f6c6f6d626f2f7061796d6f2d6170692d706870)](https://github.com/jcolombo/paymo-api-php/issues)[![GitHub Stars](https://camo.githubusercontent.com/db3434312d4557a3d2f56dc80d69db0585a444c0eb069c5ca940e8e99c90387c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f6a636f6c6f6d626f2f7061796d6f2d6170692d706870)](https://github.com/jcolombo/paymo-api-php/stargazers)

---

Overview
--------

[](#overview)

This independently developed package provides a developer-friendly toolkit to simplify all interactions with the Paymo REST API. It is not affiliated with or endorsed by Paymo.

**Official Paymo API Documentation:**

---

Features
--------

[](#features)

- **Full CRUD Operations** - Create, Read, Update, and Delete for all 38 Paymo resource types
- **Fluent Interface** - Chainable methods for clean, readable code
- **Smart Query Building** - WHERE filters, HAS relationship conditions, and INCLUDE for eager loading
- **JSON-Ready Collections** - Collections are directly JSON-serializable for API responses
- **Server-Side Pagination** - Limit and paginate large result sets via `limit()` method
- **Response Caching** - Built-in file-based caching to reduce API calls and avoid rate limits
- **Request Logging** - Comprehensive logging for debugging and monitoring
- **Type Safety** - Property type validation for each resource type
- **Relationship Support** - Load related entities in a single call
- **File Uploads** - Easy image and file attachment handling

---

Requirements
------------

[](#requirements)

- PHP 7.4 or higher
- An active [Paymo](https://www.paymoapp.com) account
- A Paymo API key (found in your Paymo account settings)
- Composer

---

Installation
------------

[](#installation)

```
composer require jcolombo/paymo-api-php
```

---

Quick Start
-----------

[](#quick-start)

### Establishing a Connection

[](#establishing-a-connection)

```
use Jcolombo\PaymoApiPhp\Paymo;
use Jcolombo\PaymoApiPhp\Entity\Resource\Project;

// Connect with your API key
$paymo = Paymo::connect('YOUR_API_KEY');

// Alternative: Username/password authentication (API key recommended; see Session resource for token-based auth)
$paymo = Paymo::connect(['username', 'password']);
```

### Fetching a Single Resource

[](#fetching-a-single-resource)

```
use Jcolombo\PaymoApiPhp\Entity\Resource\Project;

// Fetch a project by ID
$project = Project::new()->fetch(12345);

// Access properties directly
echo $project->name;
echo $project->description;

// Fetch with related entities included
$project = Project::new()->fetch(12345, ['client', 'tasklists', 'tasks']);
echo $project->client->name; // Access the related client
```

### Fetching Collections (Lists)

[](#fetching-collections-lists)

```
use Jcolombo\PaymoApiPhp\Entity\Resource\Project;
use Jcolombo\PaymoApiPhp\Entity\Resource\Task;

// Get all projects
$projects = Project::list()->fetch();

foreach ($projects as $project) {
    echo $project->name . "\n";
}

// Get count directly
echo "Total projects: " . count($projects);

// JSON encode directly for API responses
$json = json_encode($projects);  // Returns array of flattened objects

// Get all tasks with filters
$tasks = Task::list()
    ->where(Task::where('complete', false))
    ->fetch();

// Paginate results (get only first 100)
$invoices = Invoice::list()->limit(100)->fetch();

// Paginate with explicit page (page 2, 50 per page)
$invoices = Invoice::list()->limit(2, 50)->fetch();
```

### Creating Resources

[](#creating-resources)

```
use Jcolombo\PaymoApiPhp\Entity\Resource\Project;
use Jcolombo\PaymoApiPhp\Entity\Resource\Task;

// Create a new project
$project = new Project();
$project->name = "My New Project";
$project->description = "Project description here";
$project->create();

// Fluent style creation
$project = Project::new()
    ->set(['name' => 'Another Project', 'description' => 'Created with chaining'])
    ->create();

// Create a task (requires tasklist_id or project_id)
$task = Task::new()
    ->set([
        'name' => 'My First Task',
        'tasklist_id' => 123,
        'description' => 'Task details here'
    ])
    ->create();
```

### Updating Resources

[](#updating-resources)

```
// Fetch, modify, and update
$project = Project::new()->fetch(12345);
$project->name = "Updated Project Name";
$project->description = "New description";
$project->update();

// Only dirty (changed) fields are sent to the API
```

### Deleting Resources

[](#deleting-resources)

```
// Delete via instance
$project = Project::new()->fetch(12345);
$project->delete();

// Delete by ID directly
Project::deleteById(12345);
```

---

Supported Resources
-------------------

[](#supported-resources)

The SDK supports all 38 Paymo API resource types:

CategoryResources**Projects &amp; Tasks**`Project`, `ProjectStatus`, `ProjectTemplate`, `Tasklist`, `Task`, `Subtask`, `TaskAssignment`**Time Tracking**`TimeEntry`, `Booking`**Financial**`Invoice`, `InvoiceItem`, `InvoicePayment`, `InvoiceTemplate`, `Estimate`, `EstimateItem`, `EstimateTemplate`, `Expense`**Recurring**`RecurringProfile`, `RecurringProfileItem`, `TaskRecurringProfile`**Users &amp; Clients**`User`, `Client`, `ClientContact`, `Company`**Collaboration**`Discussion`, `Comment`, `CommentThread`, `Milestone`**Workflows**`Workflow`, `WorkflowStatus`**Files &amp; Sessions**`File`, `Session`**Reports**`Report`**Templates**`ProjectTemplateTask`, `ProjectTemplateTasklist`, `EstimateTemplateGallery`, `InvoiceTemplateGallery`**Integrations**`Webhook`---

Query Building
--------------

[](#query-building)

### WHERE Filters

[](#where-filters)

Filter collections using the static `where()` method on any resource class:

```
use Jcolombo\PaymoApiPhp\Entity\Resource\Project;
use Jcolombo\PaymoApiPhp\Entity\Resource\Task;

// Simple equality
$projects = Project::list()
    ->where(Project::where('active', true))
    ->fetch();

// With operators
$tasks = Task::list()
    ->where(Task::where('complete', false, '='))
    ->where(Task::where('due_date', '2024-12-31', ''))
    ->fetch();

// Projects with more than 5 milestones
$projects = Project::list()
    ->where(Project::has('milestones', 5, '>'))
    ->fetch();
```

### Including Related Entities

[](#including-related-entities)

Eager-load related entities in a single API call:

```
// Include single relations
$project = Project::new()->fetch(12345, ['client']);

// Include multiple relations
$project = Project::new()->fetch(12345, ['client', 'tasklists', 'tasks', 'milestones']);

// Access included relations
echo $project->client->name;
foreach ($project->tasks as $task) {
    echo $task->name;
}
```

---

Configuration
-------------

[](#configuration)

### Configuration File

[](#configuration-file)

Create a `paymoapi.config.json` file in your project root to customize behavior:

```
{
  "connection": {
    "url": "https://app.paymoapp.com/api/",
    "defaultName": "PaymoApi",
    "verify": false,
    "timeout": 15.0
  },
  "path": {
    "cache": "/path/to/cache/directory",
    "logs": "/path/to/logs/directory"
  },
  "enabled": {
    "cache": true,
    "logging": true
  },
  "log": {
    "connections": false,
    "requests": true
  },
  "devMode": false
}
```

### Configuration Options

[](#configuration-options)

OptionTypeDefaultDescription`connection.url`string`https://app.paymoapp.com/api/`Paymo API base URL`connection.timeout`float`15.0`Request timeout in seconds`connection.verify`bool`false`SSL certificate verification`enabled.cache`bool`false`Enable response caching`enabled.logging`bool`false`Enable request/response logging`log.connections`bool`false`Log connection events`log.requests`bool`true`Log API requests`devMode`bool`false`Enable development mode validations`rateLimit.enabled`bool`true`Enable automatic rate limiting`rateLimit.minDelayMs`int`200`Minimum delay between requests (milliseconds)`rateLimit.safetyBuffer`int`1`Start throttling when remaining requests drops to this level`rateLimit.maxRetries`int`3`Maximum retries for 429 responses`rateLimit.retryDelayMs`int`1000`Delay before retrying after a 429 response (milliseconds)---

Caching
-------

[](#caching)

The SDK includes a built-in caching system to reduce API calls and help avoid rate limits.

### Enable Caching

[](#enable-caching)

```
{
  "enabled": {
    "cache": true
  },
  "path": {
    "cache": "/path/to/cache/directory"
  }
}
```

Or define the cache path via constant before loading the SDK:

```
define('PAYMOAPI_REQUEST_CACHE_PATH', '/path/to/cache');
```

### Cache Control

[](#cache-control)

```
use Jcolombo\PaymoApiPhp\Cache\Cache;

// Set cache lifespan (default: 300 seconds / 5 minutes)
Cache::lifespan(600); // 10 minutes

// Skip cache for a specific request
$project = Project::new()->fetch(12345, [], ['skipCache' => true]);

// Ignore cache on an entity
$project = Project::new()->ignoreCache(true)->fetch(12345);

// Custom cache handlers
Cache::registerCacheMethods(
    function($key, $lifespan) { /* fetch logic */ },
    function($key, $data, $lifespan) { /* store logic */ }
);
```

---

File Uploads
------------

[](#file-uploads)

### Uploading Images

[](#uploading-images)

```
// Upload an image to an existing entity (like a client logo)
$client = Client::new()->fetch(123);
$client->image('/path/to/logo.png');

// Specify the property key if needed
$user = User::new()->fetch(456);
$user->image('/path/to/avatar.jpg', 'image');
```

### Uploading Files

[](#uploading-files)

```
// Attach a file to an entity
$task = Task::new()->fetch(789);
$task->file('/path/to/document.pdf');
```

---

Working with Properties
-----------------------

[](#working-with-properties)

### Getting and Setting

[](#getting-and-setting)

```
$project = Project::new()->fetch(12345);

// Get single property
$name = $project->get('name');

// Get multiple properties
$data = $project->get(['name', 'description', 'active']);

// Set single property
$project->set('name', 'New Name');

// Set multiple properties
$project->set([
    'name' => 'New Name',
    'description' => 'New description'
]);
```

### Dirty Tracking

[](#dirty-tracking)

The SDK tracks which properties have been modified since the last save/load:

```
$project = Project::new()->fetch(12345);
$project->name = "Changed Name";

// Check if any properties are dirty
if ($project->isDirty()) {
    $project->update(); // Only sends changed fields
}

// Get list of dirty property keys
$dirtyKeys = $project->getDirtyKeys(); // ['name']

// Get dirty values with original and current
$dirtyValues = $project->getDirtyValues();
// ['name' => ['original' => 'Old Name', 'current' => 'Changed Name']]
```

### Flattening to stdClass

[](#flattening-to-stdclass)

Export entity data as a plain PHP object:

```
$project = Project::new()->fetch(12345, ['client', 'tasks']);

// Get as stdClass (includes relations)
$data = $project->flatten();

// Strip null values
$data = $project->flatten(['stripNull' => true]);
```

### JSON Serialization

[](#json-serialization)

Collections can be directly JSON-encoded for API responses:

```
$projects = Project::list()->fetch(['id', 'name', 'active']);

// Direct JSON encoding - collections implement JsonSerializable
echo json_encode($projects);
// Output: [{"id": 123, "name": "Project A", "active": true}, ...]

// Assign directly to response data structures
$response->projects = $projects;  // Auto-serializes correctly
```

---

Error Handling
--------------

[](#error-handling)

The SDK throws exceptions for common error scenarios:

```
use Jcolombo\PaymoApiPhp\Entity\Resource\Project;

try {
    // Missing required field
    $project = Project::new()->create(); // Throws: requires 'name'

    // Fetch without ID
    $project = Project::new()->fetch(); // Throws: requires ID

    // Update without ID
    $project = Project::new();
    $project->name = "Test";
    $project->update(); // Throws: requires ID

} catch (\Exception $e) {
    echo "Error: " . $e->getMessage();
}
```

### Protecting Dirty Data

[](#protecting-dirty-data)

Prevent accidental overwrites of unsaved changes:

```
$project = Project::new()->fetch(12345);
$project->name = "Unsaved change";
$project->protectDirtyOverwrites(true);

// This will throw an exception because there are dirty fields
$project->fetch(12345); // Throws exception
```

---

Pagination
----------

[](#pagination)

> **Note:** This is an undocumented Paymo API feature. See `OVERRIDES.md` for details.

The SDK supports server-side pagination for collection fetches:

```
use Jcolombo\PaymoApiPhp\Entity\Resource\Invoice;

// Fetch only the first 100 results (page 0 implied)
$invoices = Invoice::list()->limit(100)->fetch();

// Fetch page 2 with 50 results per page
$invoices = Invoice::list()->limit(2, 50)->fetch();

// Combine with filters
$tasks = Task::list()
    ->limit(25)
    ->fetch(['name'], [Task::where('complete', false)]);

// Iterate through all pages
$page = 0;
$pageSize = 100;
$allInvoices = [];

do {
    $invoices = Invoice::list()->limit($page, $pageSize)->fetch();
    $results = $invoices->raw();
    $allInvoices = array_merge($allInvoices, $results);
    $page++;
} while (count($results) === $pageSize);
```

**Key Points:**

- Pages are **0-indexed** (first page is 0)
- `limit(100)` = page 0, 100 results
- `limit(2, 50)` = page 2, 50 results per page
- API does NOT return total count - track pages manually
- WHERE conditions apply before pagination

---

Rate Limiting
-------------

[](#rate-limiting)

Paymo enforces API rate limits. The SDK includes a built-in 200ms minimum delay between requests to help prevent hitting rate limits. For high-volume operations:

1. **Enable caching** to reduce redundant API calls
2. **Use `skipCache` sparingly** - only when you need fresh data
3. **Batch operations** where possible by including related entities
4. **Use pagination** to process large datasets in chunks

---

Advanced Usage
--------------

[](#advanced-usage)

### Multiple Connections

[](#multiple-connections)

```
// Connect to multiple Paymo accounts
$connection1 = Paymo::connect('API_KEY_1', null, 'Account1');
$connection2 = Paymo::connect('API_KEY_2', null, 'Account2');

// Use specific connection for entities
$project = new Project($connection1);
$project->fetch(12345);
```

### Resource Property Types

[](#resource-property-types)

Each resource defines its property types for validation:

```
// Property types include:
// - 'text', 'integer', 'decimal', 'boolean'
// - 'date', 'datetime'
// - 'resource:entityname' - foreign key reference
// - 'collection:entityname' - array of related entities
// - 'intEnum:25|50|75|100' - enumerated integer values
```

### Read-Only and Create-Only Properties

[](#read-only-and-create-only-properties)

```
// READONLY properties (like 'id', 'created_on') cannot be set
// CREATEONLY properties can only be set during create(), not update()

$task = Task::new();
$task->project_id = 123;  // CREATEONLY - can set before create()
$task->name = "My Task";
$task->create();

$task->project_id = 456;  // Ignored - cannot change after creation
$task->update();
```

---

Contributing
------------

[](#contributing)

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

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

---

License
-------

[](#license)

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

---

Credits
-------

[](#credits)

Developed and maintained by [Joel Colombo](mailto:jc-dev@360psg.com) at [360 PSG, Inc.](https://360psg.com)

---

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md) for a detailed history of changes.

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance85

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 98.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~210 days

Recently: every ~95 days

Total

11

Last Release

162d ago

PHP version history (3 changes)v0.0.1PHP &gt;=7.1

v0.5.0PHP &gt;=7.2

v0.5.7PHP &gt;=7.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/35b4a899dcb0f25a0b114361713f56db6544facda0fc982386ede3d8ef2cc6ec?d=identicon)[jcolombo](/maintainers/jcolombo)

---

Top Contributors

[![jcolombo](https://avatars.githubusercontent.com/u/1483186?v=4)](https://github.com/jcolombo "jcolombo (149 commits)")[![mtwhelan](https://avatars.githubusercontent.com/u/433916?v=4)](https://github.com/mtwhelan "mtwhelan (2 commits)")

---

Tags

paymopaymo apppaymo php

### Embed Badge

![Health badge](/badges/jcolombo-paymo-api-php/health.svg)

```
[![Health](https://phpackages.com/badges/jcolombo-paymo-api-php/health.svg)](https://phpackages.com/packages/jcolombo-paymo-api-php)
```

###  Alternatives

[neuron-core/neuron-ai

The PHP Agentic Framework.

1.8k245.3k21](/packages/neuron-core-neuron-ai)[tencentcloud/tencentcloud-sdk-php

TencentCloudApi php sdk

3731.2M42](/packages/tencentcloud-tencentcloud-sdk-php)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

245.2k](/packages/aedart-athenaeum)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
