API Referencev1

PHP SDK

The official Odento PHP SDK provides a type-safe interface to the Odento API. PSR-18 compatible HTTP client. Requires PHP 8.0 or later. Available on Packagist.

Installation

Requestbash
composer require odento/sdk

# or manually add to composer.json
{
    "require": {
        "odento/sdk": "^1.0"
    }
}

Initialization

Create a client instance with your API key credentials. Load keys from environment variables — never hardcode them in your source:

Requestphp
use Odento\SDK\OdentoClient;

$client = new OdentoClient([
    'key_id'     => getenv('ODENTO_API_KEY_ID'),       // ok_live_...
    'key_secret' => getenv('ODENTO_API_KEY_SECRET'),   // os_...
]);

// With custom options
$stagingClient = new OdentoClient([
    'key_id'     => getenv('ODENTO_API_KEY_ID'),
    'key_secret' => getenv('ODENTO_API_KEY_SECRET'),
    'base_url'   => 'https://staging-api.odento.app',
    'timeout'    => 60,
    'max_retries' => 5,
]);

JWT Authentication

For user-facing applications, authenticate with email and password. The SDK automatically manages token storage and refresh:

Requestphp
use Odento\SDK\OdentoClient;

$client = new OdentoClient();

// Login — SDK stores tokens internally
$auth = $client->auth()->login([
    'email'    => 'dr.sharma@dentalkare.in',
    'password' => 'your-secure-password',
]);

echo "Logged in as: " . $auth->user->name . "\n";

// All subsequent calls use the stored access token
// The SDK automatically refreshes when the token expires

Usage Examples

List Patients

Requestphp
$result = $client->patients()->list([
    'limit'  => 10,
    'status' => 'active',
]);

foreach ($result->items as $patient) {
    echo $patient->id . ': ' . $patient->first_name . ' ' . $patient->last_name . "\n";
}

echo "Has more: " . ($result->pagination->has_more ? 'true' : 'false') . "\n";

// Iterate through all pages
$cursor = $result->pagination->next_cursor;
while ($result->pagination->has_more) {
    $result = $client->patients()->list(['limit' => 10, 'cursor' => $cursor]);
    foreach ($result->items as $patient) {
        echo $patient->id . ': ' . $patient->first_name . "\n";
    }
    $cursor = $result->pagination->next_cursor;
}

Create a Patient

Requestphp
$patient = $client->patients()->create([
    'first_name'     => 'Priya',
    'last_name'      => 'Sharma',
    'email'          => 'priya.sharma@example.com',
    'phone'          => '+919876543210',
    'date_of_birth'  => '1990-05-15',
    'gender'         => 'female',
    'address' => [
        'street'      => '12 MG Road',
        'city'        => 'Bengaluru',
        'state'       => 'Karnataka',
        'postal_code' => '560001',
        'country'     => 'India',
    ],
]);

echo "Created patient: " . $patient->id . "\n"; // pat_5f8d3a2b1c9e4a7b

Create an Appointment

Requestphp
$appointment = $client->appointments()->create([
    'patient_id'  => 'pat_5f8d3a2b1c9e4a7b',
    'dentist_id'  => 'usr_abc123',
    'start_time'  => '2025-01-22T10:00:00Z',
    'end_time'    => '2025-01-22T11:00:00Z',
    'type'        => 'consultation',
    'location_id' => 'loc_xyz789',
    'notes'       => 'Routine checkup',
]);

echo "Created appointment: " . $appointment->id . "\n"; // apt_7a3b9c1d2e5f8a4b

Create an Invoice

Requestphp
$invoice = $client->billing()->invoices()->create([
    'patient_id' => 'pat_5f8d3a2b1c9e4a7b',
    'items' => [
        ['description' => 'Dental consultation', 'quantity' => 1, 'unit_price' => 50000],
        ['description' => 'Scaling and polishing', 'quantity' => 1, 'unit_price' => 150000],
    ],
    'due_date' => '2025-02-01',
]);

echo "Created invoice: {$invoice->id} ({$invoice->number}) total: {$invoice->total}\n";
// Created invoice: inv_4a7b9c1d2e5f8a3b (INV-2025-0042) total: 236000

Record a Payment

Requestphp
$payment = $client->billing()->invoices()->recordPayment('inv_4a7b9c1d2e5f8a3b', [
    'amount'    => 59000,  // in paise
    'method'   => 'upi',
    'reference' => 'UPI-987654321',
]);

echo "Payment recorded: {$payment->id}\n";
echo "Invoice status: {$payment->invoice_status}\n"; // paid

Error Handling

The SDK throws typed exceptions. Catch specific exception types to handle different error cases:

Requestphp
use Odento\SDK\Exceptions\OdentoException;
use Odento\SDK\Exceptions\ValidationException;
use Odento\SDK\Exceptions\RateLimitException;

try {
    $patient = $client->patients()->create([
        'first_name' => 'Priya',
        // missing required fields...
    ]);
} catch (ValidationException $e) {
    echo "Validation failed: " . $e->getMessage() . "\n";
    foreach ($e->getDetails() as $detail) {
        echo "  {$detail->field}: {$detail->message}\n";
    }
} catch (RateLimitException $e) {
    echo "Rate limited. Retry after {$e->retryAfter}s\n";
} catch (OdentoException $e) {
    echo "API error ({$e->getCode()}): {$e->getMessage()}\n";
}

Configuration Options

OptionTypeDefaultDescription
key_idstring—API key ID (ok_live_*)
key_secretstring—API key secret (os_*)
base_urlstringapi.odento.appBase URL for API requests
timeoutint30Request timeout in seconds
max_retriesint3Max retries for 5xx and 429 errors
auto_refreshbooltrueAuto-refresh JWT tokens before expiry

Laravel Integration

For Laravel applications, register the SDK as a singleton in your service provider:

Requestphp
// app/Providers/AppServiceProvider.php
use Odento\SDK\OdentoClient;

public function register()
{
    $this->app->singleton(OdentoClient::class, function ($app) {
        return new OdentoClient([
            'key_id'     => config('services.odento.key_id'),
            'key_secret' => config('services.odento.key_secret'),
        ]);
    });
}

// config/services.php
'odento' => [
    'key_id'     => env('ODENTO_API_KEY_ID'),
    'key_secret' => env('ODENTO_API_KEY_SECRET'),
],

Then inject the client in your controllers:

Requestphp
use Odento\SDK\OdentoClient;

class PatientController extends Controller
{
    public function __construct(private OdentoClient $odento) {}

    public function index()
    {
        $result = $this->odento->patients()->list(['limit' => 20]);
        return response()->json($result);
    }
}

Was this page helpful?

Help us improve our documentation