KA

kayako-api-function

Scaffolds new API functions in Kayako with built-in validation and secure error handling blocks.

Install

mkdir -p .claude/skills/kayako-api-function && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/12321" && unzip -o skill.zip -d .claude/skills/kayako-api-function && rm skill.zip

Installs to .claude/skills/kayako-api-function

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Adds a new procedural API function to `src/api.php` following the project's validation-first, try/catch-per-operation pattern. Initializes Kayako SOAP config, wraps each Kayako call in its own try/catch, returns status/status_text arrays. Use when user says 'add API function', 'new ticket operation', 'create endpoint in api.php', or 'add support function'. Do NOT use for modifying `src/Plugin.php` hook registration or for class-based API work.
447 chars✓ has a “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Advanced

Key capabilities

  • Add new procedural API functions to src/api.php
  • Enforce validation-first input checks
  • Wrap Kayako SDK calls in individual try/catch blocks
  • Check user ownership before mutating tickets
  • Initialize Kayako SOAP configuration
  • Return status/status_text arrays on all function paths

How it works

The skill adds a new procedural API function by defining it in src/api.php, initializing a result array, validating inputs, checking ownership, initializing Kayako SOAP, and wrapping each Kayako operation in a try/catch block.

Inputs & outputs

You give it
Request to add a new API function with parameters
You get back
A new procedural API function in src/api.php following project standards

When to use kayako-api-function

  • Adding new API endpoints
  • Creating ticket operations
  • Extending support functions
  • Implementing secure API logic

About this skill

kayako-api-function

Critical

  • Never interpolate $_GET/$_POST directly into queries — always $db->real_escape($input).
  • Every Kayako SDK call must be in its own try/catch (Exception $e) block — one operation per block.
  • Always check $GLOBALS['tf']->ima != 'admin' AND account_lid before any mutation that touches another user's ticket.
  • Always call function_requirements('class.kyConfig') before the SOAP init try/catch — not inside it.
  • Return the $result array on every path — never let the function fall off the end without returning.

Instructions

  1. Define the function in src/api.php with a PHPDoc block. Use camelCase names matching existing functions (openTicket, viewTicket, ticketPost, getTicketList).

    /**
     * One-line description.
     *
     * @param int    $ticketID  the ticket id
     * @param string $content   the reply body
     * @return array status/status_text result
     */
    function myNewFunction($ticketID, $content)
    {
    
  2. Initialize the result array as the first statement. Include only keys this function actually returns — do not add speculative keys.

        $result = [
            'status'      => 'incomplete',
            'status_text' => '',
        ];
    
  3. Validate all required inputs with early-return guards before touching Kayako. Return 'Failed' (capital F) for user-input errors.

        if (!$ticketID) {
            $result['status']      = 'Failed';
            $result['status_text'] = 'Ticket Reference ID is required. Please try again!';
            return $result;
        }
        if (!$content) {
            $result['status']      = 'Failed';
            $result['status_text'] = 'Content is required. Please try again!';
            return $result;
        }
    

    Verify all validation guards return before proceeding to Step 4.

  4. Check ownership for any read/mutate of another user's ticket (skip for creation functions):

        if ($GLOBALS['tf']->ima != 'admin' &&
            $GLOBALS['tf']->accounts->data['account_lid'] != kyTicket::get($ticketID)->getUser()->getEmail()) {
            $result['status']      = 'Failed';
            $result['status_text'] = 'Access denied. Please try again!';
            myadmin_log('api', 'info', 'Denied: ' . $GLOBALS['tf']->accounts->data['account_lid'], __LINE__, __FILE__);
            return $result;
        }
    

    Wrap the ownership check itself in try/catchkyTicket::get() can throw.

  5. Initialize Kayako SOAP — call function_requirements outside the try, init inside:

        function_requirements('class.kyConfig');
        try {
            kyConfig::set(new kyConfig(KAYAKO_API_URL, KAYAKO_API_KEY, KAYAKO_API_SECRET));
            kyConfig::get()->setDebugEnabled(false)->setTimeout(120);
        } catch (Exception $e) {
            $result['status']      = 'failed';
            $result['status_text'] = 'Kayako exception occurred setting config options. Please try again!';
            myadmin_log('api', 'info', $e->getMessage(), __LINE__, __FILE__);
            return $result;
        }
    
  6. Wrap each subsequent Kayako operation in its own try/catch. Set 'status_text' to a human message naming the failing operation:

        try {
            $ticket = kyTicket::get($ticketID);
            $user   = $ticket->getUser();
        } catch (Exception $e) {
            $result['status']      = 'Failed';
            $result['status_text'] = 'Kayako exception occurred getting ticket detail. Please try again!';
            myadmin_log('api', 'info', $e->getMessage(), __LINE__, __FILE__);
            return $result;
        }
        try {
            $post = $ticket->newPost($user, $content)->create();
            if ($post) {
                $result['status']      = 'Success';
                $result['status_text'] = 'Post added successfully';
            } else {
                $result['status']      = 'Failed';
                $result['status_text'] = 'Exception occurred adding post.';
            }
            return $result;
        } catch (Exception $e) {
            $result['status']      = 'Failed';
            $result['status_text'] = 'Kayako exception occurred adding post. Please try again!';
            myadmin_log('api', 'info', $e->getMessage(), __LINE__, __FILE__);
            return $result;
        }
    }
    
  7. For DB-only functions (no Kayako, e.g. listing from swtickets), use clone $GLOBALS['helpdesk_dbh'] and always escape:

        $db = clone $GLOBALS['helpdesk_dbh'];
        $db->query("SELECT * FROM swtickets WHERE ticketmaskid = '" . $db->real_escape($ticketID) . "'", __LINE__, __FILE__);
    
  8. Run tests to verify the function exists and its signature is correct:

    vendor/bin/phpunit tests/ApiFunctionsTest.php
    

    Add tests to tests/ApiFunctionsTest.php following the ReflectionFunction pattern: verify function exists, parameter count, parameter names, and validation-failure return shape.

Examples

User says: "Add a function to close a ticket by ID."

Actions taken:

  1. Add closeTicket($ticketID) to src/api.php
  2. $result = ['status' => 'incomplete', 'status_text' => '']
  3. Guard: if (!$ticketID) → return 'Failed'
  4. Ownership check in try/catch
  5. SOAP init block
  6. try { kyTicket::get($ticketID)->close()->save(); $result['status'] = 'Success'; ... return $result; } catch ...
  7. Add testCloseTicketFunctionExists, testCloseTicketSignature, testCloseTicketFailsWithEmptyId to tests/ApiFunctionsTest.php
  8. Run vendor/bin/phpunit tests/ApiFunctionsTest.php

Result: New function in src/api.php, matching shape of ticketPost; tests green.

Common Issues

  • Call to undefined function kyConfig::set(): function_requirements('class.kyConfig') was not called before the SOAP init block. Add it immediately before the try/catch.
  • Undefined variable $result in catch block: $result array was not initialized before the first if-guard. Move the $result = [...] declaration to the very first line of the function body.
  • Tests fail with openTicket not found: setUpBeforeClass only calls require_once when openTicket doesn't exist. Add a stub for any new global functions your function calls (e.g. ticket_status_all) inside the if (!function_exists('openTicket')) block in ApiFunctionsTest::setUpBeforeClass().
  • status key returns lowercase 'failed' instead of 'Failed': Validation-path failures use capital-F 'Failed'; only SOAP init failures in openTicket/getTicketList use lowercase. Match the convention of the nearest sibling function.
  • Undefined index: account_lid: $GLOBALS['tf']->accounts->data is not available in unit tests — wrap ownership checks in try/catch and confirm the test only exercises the pre-ownership validation paths.

When not to use it

  • For modifying src/Plugin.php hook registration
  • For class-based API work
  • When interpolating $_GET/$_POST directly into queries

Limitations

  • Does not modify src/Plugin.php
  • Does not support class-based API work
  • Requires manual addition of unit tests

How it compares

This skill enforces strict coding standards for API functions, including validation and error handling, which is more structured and secure than ad-hoc function creation.

Compared to similar skills

kayako-api-function side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
kayako-api-function (this skill)04moReviewAdvanced
laravel-specialist123moNo flagsIntermediate
developing-with-turbo-streams15moNo flagsIntermediate
payment-integration16moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

laravel-specialist

Jeffallan

Use when building Laravel 10+ applications requiring Eloquent ORM, API resources, or queue systems. Invoke for Laravel models, Livewire components, Sanctum authentication, Horizon queues.

1215

developing-with-turbo-streams

hotwired-laravel

Basics of developing with Turbo Streams in web applications. Activate when working on projects that utilize Turbo Streams for enhancing user experience through real-time updates, dynamic content changes, and partial page updates without full reloads.

15

payment-integration

mrgoonie

Integrate payments with SePay (VietQR), Polar, Stripe, Paddle (MoR subscriptions), Creem.io (licensing). Checkout, webhooks, subscriptions, QR codes, multi-provider orders.

13

developing-with-prism

prism-php

Guide for developing with Prism PHP package - a Laravel package for integrating LLMs. Activate or use when working with Prism features including text generation, structured output, embeddings, image generation, audio processing, streaming, tools/function calling, or any LLM provider integration (OpenAI, Anthropic, Gemini, Mistral, Groq, XAI, DeepSeek, OpenRouter, Ollama, VoyageAI, ElevenLabs). Activate for any Prism-related development tasks.

12

restapi-translations

mikopbx

Управление переводами REST API ключей (rest_*) для MikoPBX. Автоматически находит отсутствующие русские ключи в RestApi.php и синхронизирует их с исходным кодом. Использовать при проверке переводов API, после добавления новых endpoints или перед релизом.

11

moodle-external-api-development

davila7

Create custom external web service APIs for Moodle LMS. Use when implementing web services for course management, user tracking, quiz operations, or custom plugin functionality. Covers parameter validation, database operations, error handling, service registration, and Moodle coding standards.

10

Search skills

Search the agent skills registry