PHPackages                             jcolombo/niftyquoter-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. [HTTP &amp; Networking](/categories/http)
4. /
5. jcolombo/niftyquoter-api-php

ActiveLibrary[HTTP &amp; Networking](/categories/http)

jcolombo/niftyquoter-api-php
============================

PHP SDK for the NiftyQuoter sales proposal API

v0.5.0-alpha(1mo ago)01↑2900%MITPHPPHP &gt;=8.1CI passing

Since Apr 5Pushed 1mo agoCompare

[ Source](https://github.com/jcolombo/niftyquoter-api-php)[ Packagist](https://packagist.org/packages/jcolombo/niftyquoter-api-php)[ Docs](https://github.com/jcolombo/niftyquoter-api-php)[ RSS](/packages/jcolombo-niftyquoter-api-php/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (2)Versions (2)Used By (0)

NiftyQuoter API for PHP
=======================

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

A PHP SDK for the [NiftyQuoter](https://niftyquoter.com) sales proposal API.

[![Latest Version](https://camo.githubusercontent.com/b30cecd462a32a75c1f7c18744823f515f6aa57e0fdc70f7df06fd407a526e01/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6a636f6c6f6d626f2f6e6966747971756f7465722d6170692d7068702e737667)](https://packagist.org/packages/jcolombo/niftyquoter-api-php)[![PHP Version](https://camo.githubusercontent.com/ed8edc25c8f58f70f5e09db871ef38c6c3acf6e9189ff95b58be3282e16dbaaf/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f6a636f6c6f6d626f2f6e6966747971756f7465722d6170692d7068702e737667)](https://packagist.org/packages/jcolombo/niftyquoter-api-php)[![License](https://camo.githubusercontent.com/91b909e3b61c0177de30d3a13dde3028dd1b39479226bf901492c88f4d39fa15/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6a636f6c6f6d626f2f6e6966747971756f7465722d6170692d706870)](LICENSE)[![GitHub Issues](https://camo.githubusercontent.com/33f40699f07ae1f8bbc1172b5be51c472ca7b06df0fc787d8527d24ce6034ec4/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732f6a636f6c6f6d626f2f6e6966747971756f7465722d6170692d706870)](https://github.com/jcolombo/niftyquoter-api-php/issues)

---

Overview
--------

[](#overview)

This independently developed package provides a developer-friendly PHP toolkit for interacting with the NiftyQuoter REST API. It is not affiliated with or endorsed by NiftyQuoter.

**API Documentation:**

> **Stability Notice:** This package is in active development (v0.5.x-alpha). The API surface may change before v1.0. Pin to `^0.5` in production.

---

Features
--------

[](#features)

- **Full CRUD Operations** — Create, Read, Update, and Delete for all 10 NiftyQuoter resource types
- **Fluent Interface** — Chainable methods for clean, readable code
- **Smart Query Building** — Server-side WHERE filters and client-side HAS post-filters
- **Nested Resources** — Parent context for proposal-scoped entities (items, comments, notes, contacts, pricing tables)
- **Auto-Pagination** — Automatically fetches all pages in a collection
- **Response Caching** — Built-in file-based caching with custom backend support
- **Rate Limiting** — Dual sliding-window limiter (30/min + 1000/hr) with automatic 429 retry
- **Request Logging** — Conditional file-based logging for debugging
- **Type Coercion** — Automatic property type conversion based on resource field definitions
- **Dirty Tracking** — Only changed fields are sent on update
- **Zero Dev Dependencies** — Custom test framework requires no PHPUnit or dev packages

---

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

[](#requirements)

- PHP 8.1 or higher
- A [NiftyQuoter](https://niftyquoter.com) account with API access
- Your NiftyQuoter account email and API key
- Composer

---

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

[](#installation)

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

---

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

[](#quick-start)

### Establishing a Connection

[](#establishing-a-connection)

```
use Jcolombo\NiftyquoterApiPhp\NiftyQuoter;

// Connect with your email and API key (HTTP Basic Auth)
$nq = NiftyQuoter::connect('you@example.com', 'your-api-key');
```

The SDK uses a singleton pattern — calling `connect()` with the same credentials returns the existing connection instance.

### Fetching a Single Resource

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

```
use Jcolombo\NiftyquoterApiPhp\Entity\Resource\Client;

// Fetch a client by ID
$client = Client::new($nq)->fetch(123);

// Access properties directly
echo $client->first_name;
echo $client->email;
```

### Fetching Collections (Lists)

[](#fetching-collections-lists)

```
use Jcolombo\NiftyquoterApiPhp\Entity\Resource\Proposal;

// Get one page of proposals (default: page 1, up to 100 results)
$proposals = Proposal::list($nq)->fetch();

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

// Get count of returned results
echo "Proposals on this page: " . count($proposals);

// Explicitly fetch ALL proposals (auto-paginates through every page)
$allProposals = Proposal::list($nq)->fetchAll();

// JSON encode directly (collections implement JsonSerializable)
$json = json_encode($proposals);
```

### Creating Resources

[](#creating-resources)

```
use Jcolombo\NiftyquoterApiPhp\Entity\Resource\Client;

// Create a new client
$client = Client::new($nq);
$client->is_company = false;
$client->first_name = 'Jane';
$client->last_name = 'Doe';
$client->email = 'jane@example.com';
$client->create();

// The client now has an ID from the API
echo "Created client #{$client->id}";
```

### Updating Resources

[](#updating-resources)

```
// Fetch, modify, and update
$client = Client::new($nq)->fetch(123);
$client->email = 'new-email@example.com';
$client->phone = '555-0100';
$client->update();

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

### Deleting Resources

[](#deleting-resources)

```
$client = Client::new($nq)->fetch(123);
$deleted = $client->delete(); // Returns true on success
```

### Disconnecting

[](#disconnecting)

```
// Disconnect a specific connection
NiftyQuoter::disconnect('you@example.com', 'your-api-key');

// Disconnect all connections
NiftyQuoter::disconnect();
```

---

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

[](#supported-resources)

The SDK covers all 10 NiftyQuoter API resource types:

ResourceClassScopeNotes**Client**`Entity\Resource\Client`Top-levelCompanies and individual contacts**Proposal**`Entity\Resource\Proposal`Top-levelSales proposals (hub entity)**Comment**`Entity\Resource\Comment`Nested (Proposal)Internal comments on proposals**Note**`Entity\Resource\Note`Nested (Proposal)Notes attached to proposals**Contact**`Entity\Resource\Contact`Nested (Proposal)Client-proposal junction records**Item**`Entity\Resource\Item`Nested (Proposal or ServiceTemplate)Line items with pricing**PricingTable**`Entity\Resource\PricingTable`Nested (Proposal)Pricing table groupings**ServiceTemplate**`Entity\Resource\ServiceTemplate`Top-levelReusable service templates**EmailTemplate**`Entity\Resource\EmailTemplate`Top-levelEmail templates for proposals**TextBlock**`Entity\Resource\TextBlock`Top-levelReusable text content blocks---

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

[](#query-building)

### WHERE Filters (Server-Side)

[](#where-filters-server-side)

Filter collections using the fluent `where()` method. Available filter keys are defined per resource in their `WHERE_OPERATIONS` constant.

```
use Jcolombo\NiftyquoterApiPhp\Entity\Resource\Client;
use Jcolombo\NiftyquoterApiPhp\Entity\Resource\Proposal;

// Search clients by email
$clients = Client::list($nq)
    ->where('search_email', 'jane@example.com')
    ->fetch();

// Filter proposals by state
$sent = Proposal::list($nq)
    ->where('state', 'sent')
    ->fetch();

// Combine multiple filters
$proposals = Proposal::list($nq)
    ->where('state', 'sent')
    ->where('user_id', 42)
    ->fetch();
```

**Available WHERE filters by resource:**

ResourceFiltersClient`only_companies`, `search_email`, `search_company`, `search_first_name`, `search_last_name`, `search_phone`, `search_name`Proposal`state`, `user_id`, `currency_id`, `template_id`, `code`, `archived`, `from_date`, `to_date`### HAS Filters (Client-Side Post-Filters)

[](#has-filters-client-side-post-filters)

For properties not supported by the API's server-side filtering, use `has()` to filter results after they are fetched:

```
// Only proposals with a total value above 10000
$highValue = Proposal::list($nq)
    ->has('total_value', 10000, '>')
    ->fetch();

// Clients with a specific business name (case-insensitive contains)
$clients = Client::list($nq)
    ->has('business_name', 'acme', 'like')
    ->fetch();
```

Supported `has()` operators: `=`, `!=`, `>`, `>=`, `name = 'Web Design Package';
$item->price = '2500.00';
$item->quantity = '1';
$item->create();

// Add a comment to a proposal
$comment = Comment::new($nq)->forProposal(456);
$comment->body = 'Client approved the design phase.';
$comment->user_id = 42;
$comment->create();
```

### Collections with Parent

[](#collections-with-parent)

```
use Jcolombo\NiftyquoterApiPhp\Entity\Resource\Item;
use Jcolombo\NiftyquoterApiPhp\Entity\Resource\Comment;

// List all items for a proposal
$items = Item::list($nq)->forProposal(456)->fetch();

foreach ($items as $item) {
    echo "{$item->name}: \${$item->price}\n";
}

// List all comments for a proposal
$comments = Comment::list($nq)->forProposal(456)->fetch();
```

### Items Under Service Templates

[](#items-under-service-templates)

Items have a polymorphic parent — they can belong to either a Proposal or a ServiceTemplate:

```
// Items under a service template
$items = Item::list($nq)->forServiceTemplate(789)->fetch();

$item = Item::new($nq)->forServiceTemplate(789);
$item->name = 'Consulting Hour';
$item->price = '150.00';
$item->quantity = '1';
$item->create();
```

### Nested Resources Reference

[](#nested-resources-reference)

ResourceRequired ParentMethodsCommentProposal`forProposal($id)`NoteProposal`forProposal($id)`ContactProposal`forProposal($id)`PricingTableProposal`forProposal($id)`ItemProposal OR ServiceTemplate`forProposal($id)` or `forServiceTemplate($id)`---

Pagination
----------

[](#pagination)

By default, `fetch()` returns a **single page** of results. Use `fetchAll()` when you explicitly need every record.

```
use Jcolombo\NiftyquoterApiPhp\Entity\Resource\Client;

// Fetch one page (default: page 1, 100 results) — single API call
$clients = Client::list($nq)->fetch();

// Control page and page size
$page2 = Client::list($nq)->limit(2, 25)->fetch();

// Fetch ALL clients (auto-paginates through every page)
// Use with caution on large collections — may generate many API calls
$allClients = Client::list($nq)->fetchAll();

// Control page size for fetchAll batches
$allClients = Client::list($nq)->limit(null, 50)->fetchAll();
```

**Key points:**

- Pages are **1-indexed** (first page is 1)
- Default page size is 100 (20 for Proposals)
- `limit($page, $pageSize)` — both parameters are optional
- `fetch()` returns one page; `fetchAll()` auto-paginates until `count(results) < pageSize`

---

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

[](#configuration)

### Default Configuration

[](#default-configuration)

The SDK ships with a `default.niftyquoterapi.config.json` that is loaded automatically. You do not need to create a config file to get started.

### Custom Configuration File

[](#custom-configuration-file)

Create a `niftyquoterapi.config.json` file in your project root. The SDK merges your overrides on top of the defaults using `array_replace_recursive()` — you only need to include the keys you want to change.

```
{
  "connection": {
    "timeout": 15,
    "verify": false
  },
  "path": {
    "cache": "/tmp/niftyquoter-cache",
    "logs": "/var/log/niftyquoter"
  },
  "enabled": {
    "cache": true,
    "logging": true
  },
  "devMode": true
}
```

### Loading Configuration

[](#loading-configuration)

```
use Jcolombo\NiftyquoterApiPhp\Configuration;

// Auto-detect: pass a directory — SDK looks for niftyquoterapi.config.json in it
Configuration::overload(__DIR__);

// Or load a specific file path
Configuration::load('/path/to/my-config.json');

// Read/write config values at runtime
$timeout = Configuration::get('connection.timeout');
Configuration::set('devMode', true);
```

### Configuration Options

[](#configuration-options)

OptionTypeDefaultDescription`connection.url`string`https://api.niftyquoter.com/api/v1/`API base URL`connection.timeout`int`30`Request timeout in seconds`connection.verify`bool`true`SSL certificate verification`enabled.cache`bool`false`Enable response caching`enabled.logging`bool`false`Enable request/response logging`path.cache`string|null`null`Directory for cache files`path.logs`string|null`null`Directory for log files`log.connections`bool`false`Log connection create/destroy events`log.requests`bool`true`Log API request/response details`rateLimit.enabled`bool`true`Enable built-in rate limiter`rateLimit.minDelayMs`int`200`Minimum delay between requests (ms)`rateLimit.perMinute`int`30`Rate limit: requests per minute`rateLimit.perHour`int`1000`Rate limit: requests per hour`devMode`bool`false`Enable development-mode validations and warnings---

Caching
-------

[](#caching)

The SDK includes opt-in file-based caching to reduce API calls and avoid rate limits.

### Enable Caching

[](#enable-caching)

**Option A** — via config file:

```
{
  "enabled": { "cache": true },
  "path": { "cache": "/tmp/niftyquoter-cache" }
}
```

**Option B** — via PHP constant (must be defined before any SDK calls):

```
define('NQAPI_REQUEST_CACHE_PATH', '/tmp/niftyquoter-cache');
```

Both the constant AND `enabled.cache = true` are required for caching to activate.

### Cache Behavior

[](#cache-behavior)

- Only **GET** requests are cached
- Any **POST/PUT/DELETE** request clears the entire cache (safe default)
- Cache files are stored in `{cache_path}/nqapi-cache/` as serialized PHP
- Expiry is time-based using file modification timestamps

### Custom Cache Backend

[](#custom-cache-backend)

Replace the file-based cache with your own backend (Redis, Memcached, etc.):

```
use Jcolombo\NiftyquoterApiPhp\Cache\Cache;

Cache::registerCacheMethods(
    function (string $key) {
        // Read: return RequestResponse or null
        return Redis::get("nqapi:{$key}");
    },
    function (string $key, $data) {
        // Write: store the RequestResponse
        Redis::setex("nqapi:{$key}", 300, $data);
    },
    function () {
        // Clear: flush all SDK cache entries
        Redis::del(Redis::keys("nqapi:*"));
    }
);
```

---

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

[](#rate-limiting)

The SDK includes a built-in dual sliding-window rate limiter that operates transparently:

- **30 requests/minute** and **1000 requests/hour** (configurable)
- `waitIfNeeded()` pauses execution before each request if limits are near
- **429 responses** trigger automatic retry with exponential backoff (up to 3 retries)
- Rate limits are tracked per connection (by credential pair)

No action is needed to enable rate limiting — it is on by default. Disable it via config:

```
{
  "rateLimit": { "enabled": false }
}
```

---

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

[](#error-handling)

The SDK uses a configurable error handler rather than throwing exceptions for API errors:

```
use Jcolombo\NiftyquoterApiPhp\Utility\Error;
use Jcolombo\NiftyquoterApiPhp\Utility\ErrorSeverity;
```

### Error Severity Levels

[](#error-severity-levels)

LevelDefault HandlersDescription`NOTICE``log`Informational — non-critical issues`WARN``log`Warnings — missing fields, unexpected responses`FATAL``log`, `echo`Critical failures — connection errors, authentication failures### Customizing Error Handlers

[](#customizing-error-handlers)

Configure per-severity handlers in your config file:

```
{
  "error": {
    "handlers": {
      "notice": ["log"],
      "warn": ["log", "echo"],
      "fatal": ["log", "echo"]
    },
    "triggerPhpErrors": true
  }
}
```

When `triggerPhpErrors` is `true`, errors also trigger PHP's native error system (`trigger_error()`), allowing integration with existing error handlers.

### Checking API Response Success

[](#checking-api-response-success)

```
// CRUD methods return the entity — check if it was populated
$client = Client::new($nq)->fetch(99999);
if ($client->id === null) {
    echo "Client not found or request failed";
}

// Delete returns a boolean
$deleted = $client->delete();
if (!$deleted) {
    echo "Delete failed";
}
```

---

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

[](#working-with-properties)

### Getting and Setting

[](#getting-and-setting)

```
$client = Client::new($nq)->fetch(123);

// Magic property access
echo $client->first_name;
$client->email = 'new@example.com';

// Explicit method access
$name = $client->get('first_name');
$client->set('email', 'new@example.com');
```

### Dirty Tracking

[](#dirty-tracking)

The SDK tracks which properties have changed since the last fetch/create:

```
$client = Client::new($nq)->fetch(123);
$client->email = 'changed@example.com';

// Check if anything changed
if ($client->isDirty()) {
    $client->update(); // Only sends 'email' to the API
}

// Check a specific property
$client->isDirty('email');    // true
$client->isDirty('phone');    // false

// Get list of changed property names
$dirtyKeys = $client->getDirty(); // ['email']
```

### Serialization

[](#serialization)

```
// To associative array
$data = $client->toArray();

// To JSON string
$json = $client->toJson();

// Collections are JSON-serializable
$clients = Client::list($nq)->fetch();
echo json_encode($clients); // Array of client objects from one page

// Extract a single field from all items
$names = $clients->flatten('first_name'); // ['Jane', 'John', ...]

// Get raw keyed-by-ID array
$indexed = $clients->raw(); // [123 => Client, 456 => Client, ...]
```

---

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

[](#advanced-usage)

### Multiple Connections

[](#multiple-connections)

```
// Connect to different NiftyQuoter accounts
$account1 = NiftyQuoter::connect('user1@company.com', 'key1');
$account2 = NiftyQuoter::connect('user2@company.com', 'key2');

// Pass connection to entities
$clientsFromAccount1 = Client::list($account1)->fetch();
$clientsFromAccount2 = Client::list($account2)->fetch();
```

### Custom API Requests

[](#custom-api-requests)

For endpoints not covered by resource classes (e.g., proposal actions):

```
use Jcolombo\NiftyquoterApiPhp\Entity\Resource\Proposal;

// Send email for a proposal
$proposal = Proposal::new($nq)->fetch(456);
$result = $proposal->sendEmail(
    subject: 'Your Proposal is Ready',
    body: 'Please reviewClick the link below.',
    attachPdf: true,
    bcc: 'records@company.com'
);

// Clone a proposal
$clone = $proposal->clone(
    cloneClient: true,
    cloneComments: false,
    archiveSource: true
);
echo "Cloned as proposal #{$clone->id}";
```

### Property Types

[](#property-types)

Each resource defines typed properties that are automatically coerced:

TypePHP TypeExample Fields`text`string`name`, `email`, `body``integer`int`id`, `user_id``decimal`float`total_value`, `discount``boolean`bool`is_company`, `archived``datetime`string`created_at`, `updated_at``numeric_string`string`price`, `quantity` (Item fields)`html`string`body` (EmailTemplate)`enum:...`string`state` (Proposal)### Read-Only and Write-Only Properties

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

```
// READONLY: Set by the API, cannot be changed (id, created_at, computed fields)
// CREATEONLY: Can only be set during create(), ignored on update()
// WRITEONLY: Sent to API but never returned in responses (action triggers)

$client = Client::new($nq);
$client->is_company = true;
$client->business_name = 'Acme Corp';
$client->company_name = 'Acme Corp';  // WRITEONLY — triggers company creation
$client->create();
// company_name will not appear when you fetch this client later
```

---

Running Tests
-------------

[](#running-tests)

The SDK includes a custom test framework that runs live API tests against your NiftyQuoter account. **No dev dependencies required.**

### Setup

[](#setup)

Create a `niftyquoterapi.config.test.json` in the project root:

```
{
  "testing": {
    "email": "you@example.com",
    "api_key": "your-test-api-key"
  }
}
```

> **Warning:** Tests create and delete real data in your NiftyQuoter account. Use a test/sandbox account.

### Running

[](#running)

```
# Run all tests
composer test

# Dry run (no API calls)
composer test:dry-run

# Verbose output
composer test:verbose

# Run tests for a specific resource
./tests/validate --resource=client

# Read-only mode (only GET operations)
./tests/validate --read-only

# Stop on first failure
./tests/validate --stop-on-failure

# Skip cleanup (leave test entities for inspection)
./tests/validate --no-cleanup
```

### CLI Options

[](#cli-options)

FlagDescription`--help`Show help message`--dry-run`Simulate without API calls`--read-only`Only run GET operations`--verbose`Enable verbose output`--resource=`Test a specific resource only`--stop-on-failure`Stop after first failure`--no-cleanup`Skip cleanup of test entities`--email=`Override test email`--api-key=`Override test API key---

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

[](#contributing)

Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.

---

License
-------

[](#license)

MIT — see [LICENSE](LICENSE) 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

32

—

LowBetter than 72% of packages

Maintenance90

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity28

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% 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

Unknown

Total

1

Last Release

46d ago

### 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 (20 commits)")

---

Tags

phpapisdkrestproposalsniftyquoternifty quotersales proposals

### Embed Badge

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

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

###  Alternatives

[xeroapi/xero-php-oauth2

Xero official PHP SDK for oAuth2 generated with OpenAPI spec 3

1054.3M14](/packages/xeroapi-xero-php-oauth2)[onesignal/onesignal-php-api

A powerful way to send personalized messages at scale and build effective customer engagement strategies. Learn more at onesignal.com

34170.2k2](/packages/onesignal-onesignal-php-api)[zenditplatform/zendit-php-sdk

PHP client for Zendit API

1204.3k](/packages/zenditplatform-zendit-php-sdk)[huaweicloud/huaweicloud-sdk-php

Huawei Cloud SDK for PHP

1829.2k2](/packages/huaweicloud-huaweicloud-sdk-php)[ory/hydra-client-php

Documentation for all of Ory Hydra's APIs.

1710.8k](/packages/ory-hydra-client-php)

PHPackages © 2026

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