Apacta PHP API Client Documentation

Everything you need to know about using the Tectalic Apacta PHP API Client.


The Tectalic Apacta REST API Client is a package that provides a convenient and straightforward way to interact with the Apacta API from your PHP application.

You can purchase this package from https://tectalic.com/apis/apacta.


System Requirements

  • PHP version 7.2.5 or newer (including PHP 8.0 and 8.1)
  • PHP JSON extension installed if using PHP 7.x. As of PHP 8.0, this extension became a core PHP extension so is always enabled.
  • A PSR-18 compatible HTTP client such as ‘Guzzle’ or the ‘Symfony HTTP Client’.

Composer Installation

To install this package into your PHP project, we recommend using Composer.

Please see the detailed instructions on configuring your project to access the Tectalic Composer repository.

You will need to log into the Tectalic account that purchased the Tectalic Apacta REST API Client package to access these instructions.

Once you have followed the above instructions, install the package into your project:

composer require tectalic/apacta

Manual Installation

If you aren’t using Composer in your PHP project, you can choose to Download the latest release and install it into your PHP project manually.

If doing this, you will need to ensure that all dependencies listed in the package’s composer.json file are also installed.


After installing the Tectalic Apacta REST API Client package into your project, ensure you also have a compatible PSR-18 HTTP client such as ‘Guzzle’ or the Symfony ‘HTTP Client’.

You can use the following code sample and customize it to suit your application.

// Load your project's composer autoloader (if you aren't already doing so).
require_once(__DIR__ . '/vendor/autoload.php');
use Symfony\Component\HttpClient\Psr18Client;
use Tectalic\Apacta\Authentication;
use Tectalic\Apacta\Client;
use Tectalic\Apacta\Manager;
// Build a Tectalic Apacta REST API Client globally.
$auth = new Authentication('key');
$httpClient = new Psr18Client();
Manager::build($httpClient, $auth);
// or
// Build a Tectalic Apacta REST API Client manually.
$auth = new Authentication('key');
$httpClient = new Psr18Client();
$client = new Client($httpClient, $auth, Manager::BASE_URI);


To authenticate your API requests, you will need to provide an Authentication ($auth) object when calling Manager::build().

Authentication to the Apacta API is by API Key Header authentication.

Please see the Apacta API documentation for more details on obtaining your authentication credentials.

In the Usage code above, customize the Authentication constructor to your needs. For example, you may wish to define your credentials in an environment variable and pass it to the constructor.

Client Class

The primary class you will interact with is the Client class (Tectalic\Apacta\Client).

This Client class also contains the helper methods that let you quickly access the 43 API Handlers.

Please see below for a complete list of supported handlers and methods.

Supported API Handlers and Methods

This package supports 138 API Methods, which are grouped into 43 API Handlers.

See the table below for a full list of API Handlers and Methods.

API Handler Class and Method Name Description API Verb and URL
Cities::get() Get list of cities supported in Apacta GET /cities
Cities::cityIdGet() Get details about one city GET /cities/{city_id}
ClockingRecords::get() Get a list of clocking records GET /clocking_records
ClockingRecords::post() Create clocking record for authenticated user POST /clocking_records
ClockingRecords::clockingRecordIdGet() Details of 1 clocking_record GET /clocking_records/{clocking_record_id}
ClockingRecords::clockingRecordIdPut() Edit a clocking record PUT /clocking_records/{clocking_record_id}
ClockingRecords::clockingRecordIdDelete() Delete a clocking record DELETE /clocking_records/{clocking_record_id}
ClockingRecordsCheckout::post() Checkout active clocking record for authenticated user POST /clocking_records/checkout
Companies::get() Get a list of companies GET /companies
Companies::companyIdGet() Details of 1 company GET /companies/{company_id}
CompaniesIntegrationFeatureSettings::companyIdIntegrationFeatureSettingsGet() Get a list of integration feature settings GET /companies/{company_id}/integration_feature_settings
CompaniesIntegrationFeatureSettings::companyIdIntegrationFeatureSettingsIntegrationFeatureSettingIdGet() Show details of 1 integration feature setting GET /companies/{company_id}/integration_feature_settings/{integration_feature_setting_id}
ContactTypes::get() Get list of contact types supported in Apacta GET /contact_types
ContactTypes::contactTypeIdGet() Get details about one contact type GET /contact_types/{contact_type_id}
Contacts::get() Get a list of contacts GET /contacts
Contacts::post() Add a new contact POST /contacts
Contacts::contactIdGet() Details of 1 contact GET /contacts/{contact_id}
Contacts::contactIdPut() Edit a contact PUT /contacts/{contact_id}
Contacts::contactIdDelete() Delete a contact DELETE /contacts/{contact_id}
Currencies::get() Get list of currencies supported in Apacta GET /currencies
Currencies::currencyIdGet() Get details about one currency GET /currencies/{currency_id}
EmployeeHours::get() Used to retrieve details about the logged in user’s hours GET /employee_hours
ExpenseFiles::get() Show list of expense files GET /expense_files
ExpenseFiles::post() Add file to expense POST /expense_files
ExpenseFiles::expenseFileIdGet() Show file GET /expense_files/{expense_file_id}
ExpenseFiles::expenseFileIdPut() Edit file PUT /expense_files/{expense_file_id}
ExpenseFiles::expenseFileIdDelete() Delete file DELETE /expense_files/{expense_file_id}
ExpenseLines::get() Show list of expense lines GET /expense_lines
ExpenseLines::post() Add line to expense POST /expense_lines
ExpenseLines::expenseLineIdGet() Show expense line GET /expense_lines/{expense_line_id}
ExpenseLines::expenseLineIdPut() Edit expense line PUT /expense_lines/{expense_line_id}
ExpenseLines::expenseLineIdDelete() Delete expense line DELETE /expense_lines/{expense_line_id}
Expenses::get() Show list of expenses GET /expenses
Expenses::post() Add line to expense POST /expenses
Expenses::expenseIdGet() Show expense GET /expenses/{expense_id}
Expenses::expenseIdPut() Edit expense PUT /expenses/{expense_id}
Expenses::expenseIdDelete() Delete expense DELETE /expenses/{expense_id}
ExpensesOriginalFiles::expenseIdOriginalFilesGet() Show list of all OIOUBL files for the expense GET /expenses/{expense_id}/original_files
ExpensesOriginalFiles::expenseIdOriginalFilesFileIdGet() Show OIOUBL file GET /expenses/{expense_id}/original_files/{file_id}
FormFieldTypes::get() Get list of form field types GET /form_field_types
FormFieldTypes::formFieldTypeIdGet() Get details about single FormField GET /form_field_types/{form_field_type_id}
FormFields::post() Add a new field to a Form POST /form_fields
FormFields::formFieldIdGet() Get details about single FormField GET /form_fields/{form_field_id}
FormTemplates::get() Get array of form_templates for your company GET /form_templates
FormTemplates::formTemplateIdGet() View one form template GET /form_templates/{form_template_id}
Forms::get() Retrieve array of forms GET /forms
Forms::post() Add new form POST /forms
Forms::formIdGet() View form GET /forms/{form_id}
Forms::formIdPut() Edit a form PUT /forms/{form_id}
Forms::formIdDelete() Delete a form DELETE /forms/{form_id}
InvoiceLines::get() View list of invoice lines GET /invoice_lines
InvoiceLines::post() Add invoice POST /invoice_lines
InvoiceLines::invoiceLineIdGet() View invoice line GET /invoice_lines/{invoice_line_id}
InvoiceLines::invoiceLineIdPut() Edit invoice line PUT /invoice_lines/{invoice_line_id}
InvoiceLines::invoiceLineIdDelete() Delete invoice line DELETE /invoice_lines/{invoice_line_id}
Invoices::get() View list of invoices GET /invoices
Invoices::post() Add invoice POST /invoices
Invoices::invoiceIdGet() View invoice GET /invoices/{invoice_id}
Invoices::invoiceIdPut() Edit invoice PUT /invoices/{invoice_id}
Invoices::invoiceIdDelete() Delete invoice DELETE /invoices/{invoice_id}
MassMessagesUsers::get() View list of mass messages for specific user GET /mass_messages_users
MassMessagesUsers::massMessagesUserIdGet() View mass message GET /mass_messages_users/{mass_messages_user_id}
MassMessagesUsers::massMessagesUserIdPut() Edit mass message PUT /mass_messages_users/{mass_messages_user_id}
Materials::get() View list of all materials GET /materials
Materials::materialIdGet() View material GET /materials/{material_id}
Materials::materialIdPut() Edit material PUT /materials/{material_id}
Materials::materialIdDelete() Delete material DELETE /materials/{material_id}
MaterialsRentals::materialIdRentalsGet() Show list of rentals for specific material GET /materials/{material_id}/rentals/
MaterialsRentals::materialIdRentalsPost() Add material rental POST /materials/{material_id}/rentals/
MaterialsRentals::materialIdRentalsMaterialRentalIdGet() Show rental foor materi GET /materials/{material_id}/rentals/{material_rental_id}/
MaterialsRentals::materialIdRentalsMaterialRentalIdPut() Edit material rental PUT /materials/{material_id}/rentals/{material_rental_id}/
MaterialsRentals::materialIdRentalsMaterialRentalIdPost() Add material POST /materials/{material_id}/rentals/{material_rental_id}/
MaterialsRentals::materialIdRentalsMaterialRentalIdDelete() Delete material rental DELETE /materials/{material_id}/rentals/{material_rental_id}/
MaterialsRentalsCheckout::materialIdRentalsCheckoutPost() Checkout material rental POST /materials/{material_id}/rentals/checkout/
PaymentTermTypes::get() Get a list of payment term types GET /payment_term_types
PaymentTermTypes::paymentTermTypeIdGet() Details of 1 payment term type GET /payment_term_types/{payment_term_type_id}
PaymentTerms::get() Get a list of payment terms GET /payment_terms
PaymentTerms::paymentTermIdGet() Details of 1 payment term GET /payment_terms/{payment_term_id}
Ping::get() Check if API is up and API key works GET /ping
Products::get() List products GET /products
Products::post() Add new product POST /products
Products::productIdGet() View single product GET /products/{product_id}
Products::productIdPut() Edit a product PUT /products/{product_id}
Products::productIdDelete() Delete a product DELETE /products/{product_id}
ProjectStatuses::get() Get list of project statuses GET /project_statuses
ProjectStatuses::projectStatusIdGet() Get details about one contact type GET /project_statuses/{project_status_id}
Projects::get() View list of projects GET /projects
Projects::post() Add a project POST /projects
Projects::projectIdGet() View specific project GET /projects/{project_id}
Projects::projectIdPut() Edit a project PUT /projects/{project_id}
Projects::projectIdDelete() Delete a project DELETE /projects/{project_id}
ProjectsFiles::projectIdFilesGet() Show list of files uploaded to project GET /projects/{project_id}/files
ProjectsFiles::projectIdFilesFileIdGet() Show file GET /projects/{project_id}/files/{file_id}/
ProjectsFiles::projectIdFilesFileIdPut() Edit file PUT /projects/{project_id}/files/{file_id}/
ProjectsFiles::projectIdFilesFileIdDelete() Delete file DELETE /projects/{project_id}/files/{file_id}/
ProjectsProjectFiles::projectIdProjectFilesGet() Show list of project files uploaded to project GET /projects/{project_id}/project_files
ProjectsProjectFiles::projectIdProjectFilesPost() Add project file to projects POST /projects/{project_id}/project_files
ProjectsProjectFiles::projectIdProjectFilesProjectFileIdGet() Show project file GET /projects/{project_id}/project_files/{project_file_id}/
ProjectsProjectFiles::projectIdProjectFilesProjectFileIdPut() Edit project file PUT /projects/{project_id}/project_files/{project_file_id}/
ProjectsProjectFiles::projectIdProjectFilesProjectFileIdDelete() Delete project file DELETE /projects/{project_id}/project_files/{project_file_id}/
ProjectsUsers::projectIdUsersGet() Show list of users added to project GET /projects/{project_id}/users/
ProjectsUsers::projectIdUsersPost() Add user to project POST /projects/{project_id}/users/
ProjectsUsers::projectIdUsersUserIdGet() View specific user assigned to project GET /projects/{project_id}/users/{user_id}
ProjectsUsers::projectIdUsersUserIdDelete() Delete user from project DELETE /projects/{project_id}/users/{user_id}
StockLocations::get() List stock_locations GET /stock_locations
StockLocations::post() Add new stock_locations POST /stock_locations
StockLocations::locationIdGet() View single location GET /stock_locations/{location_id}
StockLocations::locationIdPut() Edit location PUT /stock_locations/{location_id}
StockLocations::locationIdDelete() Delete location DELETE /stock_locations/{location_id}
TimeEntries::get() List time entries GET /time_entries
TimeEntries::post() Add new time entry POST /time_entries
TimeEntries::timeEntryIdGet() View time entry GET /time_entries/{time_entry_id}
TimeEntries::timeEntryIdPut() Edit time entry PUT /time_entries/{time_entry_id}
TimeEntries::timeEntryIdDelete() Delete time entry DELETE /time_entries/{time_entry_id}
TimeEntryIntervals::get() List possible time entry intervals GET /time_entry_intervals
TimeEntryIntervals::timeEntryIntervalIdGet() View time entry interval GET /time_entry_intervals/{time_entry_interval_id}
TimeEntryTypes::get() List time entries types GET /time_entry_types
TimeEntryTypes::post() Add new time entry type POST /time_entry_types
TimeEntryTypes::timeEntryTypeIdGet() View time entry type GET /time_entry_types/{time_entry_type_id}
TimeEntryTypes::timeEntryTypeIdPut() Edit time entry type PUT /time_entry_types/{time_entry_type_id}
TimeEntryTypes::timeEntryTypeIdDelete() Delete time entry type DELETE /time_entry_types/{time_entry_type_id}
TimeEntryUnitTypes::get() List possible time entry unit types GET /time_entry_unit_types
TimeEntryUnitTypes::timeEntryUnitTypeIdGet() View time entry unit type GET /time_entry_unit_types/{time_entry_unit_type_id}
TimeEntryValueTypes::get() List possible time entry value types GET /time_entry_value_types
TimeEntryValueTypes::timeEntryValueTypeIdGet() View time entry value type GET /time_entry_value_types/{time_entry_value_type_id}
Users::get() Get list of users in company GET /users
Users::post() Add user to company POST /users
Users::userIdGet() View user GET /users/{user_id}
Users::userIdPut() Edit user PUT /users/{user_id}
Users::userIdDelete() Delete user DELETE /users/{user_id}
VendorProducts::get() List vendor products GET /vendor_products
VendorProducts::vendorProductIdGet() View single vendor product GET /vendor_products/{vendor_product_id}
WallComments::post() Add wall comment POST /wall_comments
WallComments::wallCommentIdGet() View wall comment GET /wall_comments/{wall_comment_id}
WallPosts::get() View list of wall posts GET /wall_posts
WallPosts::post() Add a wall post POST /wall_posts
WallPosts::wallPostIdGet() View wall post GET /wall_posts/{wall_post_id}
WallPostsWallComments::wallPostIdWallCommentsGet() See wall comments to a wall post GET /wall_posts/{wall_post_id}/wall_comments

Making a Request

There are two ways to make a request to the nominated API Handler and API Method:

If you built the client to be accessible globally, you can use the relevant API Handler Class directly:

use Tectalic\Apacta\Handlers\Cities;
(new Cities())->get();

Alternatively, you can access all API Handlers from the client class using the Client class:


Retrieving the Response

Once you have made a request using one of the two methods outlined above, the next step is to access the response.

You can access the response in different ways. Please choose your preferred one.

Model Responses

Model responses are Data Transfer Object (DTO) style PHP classes, with public properties for each API property.

They offer a structured way of retrieving the response from an API request.

All Response Models are an instance of \Spatie\DataTransferObject\DataTransferObject or \Spatie\DataTransferObject\DataTransferObjectCollection.

After performing the request, use the ->toModel() fluent method to the API Method:

use Tectalic\Apacta\Handlers\Cities;
$model = (new Cities())->get()->toModel();

Each API Method’s toModel() call will return the appropriate Model class type for the API Method you have just called.

Associative Array Responses

After performing the request, use the ->toArray() fluent method to the API Method:

use Tectalic\Apacta\Handlers\Cities;
$array = (new Cities())->get()->toArray();

In the resulting associative array, the array keys will match the names of the public properties in the relevant Model class.

PSR 7 Response Objects

If you need to access the raw response or inspect the HTTP headers, use the ->getResponse() fluent method to the API Method. It will return a Psr\Http\Message\ResponseInterface:

use Tectalic\Apacta\Handlers\Cities;
$response = (new Cities())->get()->getResponse();


When performing requests with Tectalic Apacta REST API Client, specific scenarios will cause a Tectalic\Apacta\Exception\ClientException to be thrown. Please see below for details.

Invalid Usage of the Manager Class

A \LogicException will be thrown if the Manager::build() function is called multiple times, or if Manager::access() is called before calling Manager::build().

Unsuccessful HTTP Response Codes

The Tectalic Apacta REST API Client depends on a PSR-18 compatible HTTP client, and that HTTP client should not throw an exception for unsuccessful HTTP response codes.

An unsuccessful response code is classified as one that is not in the range 200-299 (inclusive). Examples of unsuccessful response codes include:

  • Informational responses (100-199)
  • Redirection responses (300-399)
  • Client error responses (400-499)
  • Server error responses (500-599)

If an unsuccessful response code does occur:

  • your HTTP Client will not throw an Exception.
  • the API Handler’s toModel() method will throw a ClientException.
  • the API Handler’s toArray() method will return the response body and not throw a ClientException.
  • The API Handler’s getResponse() method will return the raw response and not throw a ClientException.

Below is an example of how you may wish to use a try/catch block when performing a request so that you can detect and handle unexpected errors.

use Tectalic\Apacta\Authentication;
use Tectalic\Apacta\Client;
use Tectalic\Apacta\ClientException;
use Tectalic\Apacta\Manager;
// Build a Tectalic Apacta REST API Client globally.
$auth = new Authentication('key');
Manager::build($httpClient, $auth);
$handler = new Cities();
// Perform a request
try {
$model = $handler->get()->toModel();
// Do something with the response model...
} catch (ClientException $e) {
// Error response received. Retrieve the HTTP response code and response body.
$responseBody = $handler->toArray();
$rawResponse = $handler->getResponse()->getResponse();
$responseCode = $handler->getResponse()->getStatusCode();
// Handle the error...

HTTP Client Exceptions

If your HTTP client of choice throws an exception other than ClientException, the Tectalic Apacta REST API Client Client and its API Handler classes will let these exceptions bubble up.

Consult your HTTP client’s documentation for more details on exception handling.


The Tectalic Apacta REST API Client package includes several types of automated PHPUnit tests to verify the correct operation:

  • Unit Tests
  • Integration Tests

To run these tests, you will need to have installed the Tectalic Apacta REST API Client package with its dev dependencies (i.e. not using the --no-dev flag when running composer).

Unit Tests

These PHPUnit tests are designed to:

  • confirm that each API Method assembles a valid request that matches the Apacta API OpenAPI specification.
  • verify the behaviour of other parts of the package, such as the Client and Manager classes.

The unit tests can be run using the following command, which needs to be run from this package’s root directory.

composer test:unit

Unit tests do not perform any real requests against the Apacta API.

Unit tests are located in the tests/Unit directory.

Integration Tests

Integration tests are located in the tests/Integration directory.

These PHPUnit tests are designed to confirm that each API Method parses a valid response, according to the Apacta API OpenAPI specification. Out of the box the integration tests are designed to work with the Prism Mock Server.

Using Prism as the Target

Make sure Prism is installed. Please see the Prism documentation for details on how to install Prism.

Once Prism is installed, you can run prism and the integration tests side by side in separate terminal windows, or using the following command, which need to be run from this package’s root directory.

echo "> Starting Prism server"
prism mock tests/openapi.json >/dev/null 2>&1 &
sleep 2
echo " => Started"
composer test:integration

Those commands will start the Prism mock server, then run the integration tests, and then stop the Prism mock server when the tests are completed.

In this case the integration tests do not perform any real requests against the Apacta API.

Using a Different Target

By setting the APACTA_CLIENT_TEST_BASE_URI environment variable, you can set a different API endpoint target for the integration tests.

For example, instead of using Prism, you can use a different mocking/staging/test server of your choice, or you can use the Apacta API’s live endpoints.

Do not forget to set the appropriate credentials in the APACTA_CLIENT_TEST_AUTH_KEY environment variable.

After your setup is complete simply run the following command.

composer test:integration

We do not recommend running integration tests against the live Apacta API endpoints. This is because the tests will send example data to all endpoints, which can result in new data being created, or existing data being deleted.


If you have any questions or feedback, you can submit a support request to the Tectalic developers by going to https://tectalic.com/support/apacta.


This software is copyright (c) Tectalic.

For the full copyright and license information, please view the ‘LICENSE’ file distributed with the source code.

Last updated 27 Oct 2022

Tectalic Apacta PHP API Client