How do I access the internal api? Where do I find "client_id" / "client_secret"

Hi I am trying to author tests for openEMR. I’ve got a local dev environment up and running on macOS Ventura.

I am very confused as to how to access the internal api of openEMR. I’ve located and opened the swagger page. However, I need authorization tokens to access the endpoints. I’ve read the API documentation, and I’m not understanding the process of gaining authorization. None of the command scripts work.

I tried this as I understood it to respond with the needed token?

curl -X POST -k -H 'Content-Type: application/x-www-form-urlencoded' -i 'https://localhost:9300/oauth2/default/token' --data 'grant_type=refresh_token&client_id=LnjqojEEjFYe5j2Jp9m9UnmuxOnMg4VodEJj3yE8_OA&refresh_token=def5020089a766d16...'

and recieved this

HTTP/1.1 401 Unauthorized
Date: Fri, 26 Jan 2024 01:02:29 GMT
Server: Apache
Set-Cookie: authserverOpenEMR=2aQDfBxL%2CyjjJrJpss5AGYRd20VgiBnOnZDI2g7RtEPd36Rs; expires=Fri, 26 Jan 2024 08:49:10 GMT; Max-Age=28000; path=/oauth2/; secure; HttpOnly; SameSite=None
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: origin, authorization, accept, content-type, x-requested-with
Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS
Access-Control-Allow-Origin: *
Set-Cookie: authserverOpenEMR=deleted; expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; path=/oauth2/; secure; HttpOnly; SameSite=None
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-XSS-Protection: 1; mode=block
Content-Length: 118
Content-Type: application/json

{“error”:“invalid_client”,“error_description”:“Client authentication failed”,“message”:“Client authentication failed”}

I feel like theres something obvious I’m missing… Can someone give me a brief step by step of gaining authorization on a local environment?

Hi Temi,

The on this suggests looking here for examples of what you are trying to accomplish. The tests folder seems to lay a decent foundation.

I am currently accessing the internal api via the command-runner interface which may be an option for you. Below is some example code from file named src/Common/Command/CreateCCDAExportCommand.php that could be run like

php ./bin/command-runner -c CreateCCDAExport

Only part of this utility is shown below, it is intended to highligh accessing the internal api.

class CreateCCDAExportCommand implements IOpenEMRCommand
    public function printUsage(CommandContext $context)
        echo "Command Usage: " . $context->getScriptName() . " -c CreateCCDAExportCommand" . "\n";

    public function getDescription(CommandContext $context): string
        return "Generates CCDA exports for all participants.";

     * Execute the command and spit any output to STDOUT and errors to STDERR
         * @param CommandContext $context All the context information needed for the CLI Command to execute
         * Loop across all participants
    public function execute(CommandContext $context)
        error_reporting(E_ALL ^ E_DEPRECATED);
        $uri = '/fhir/Patient';
        $_SESSION['authUser'] = 'admin';
        $_SESSION['authUserID'] = 1;
        $_SESSION['authProvider'] = 'Default';
        $_SERVER['HTTP_HOST'] = '';
        $_SESSION['site_id'] = 'default';
        $_SERVER['REQUEST_URI'] = $uri;
        global $ignoreAuth, $sqlconf, $GLOBALS, $_SERVER, $_SESSION;
        $ignoreAuth = true;
        $GLOBALS['OE_SITE_DIR'] = 'sites/default';
        $GLOBALS['is_local_api'] = 1;
        $GLOBALS['dbase'] = $GLOBALS['sqlconf']['dbase'];
        $GLOBALS['host'] = $GLOBALS['sqlconf']['host'];
        $GLOBALS['pass'] = $GLOBALS['sqlconf']['pass'];
        $GLOBALS['login'] = $GLOBALS['sqlconf']['login'];

        $gbl = RestConfig::GetInstance();
        $GLOBALS['system_error_logging'] = 'DEBUG'; 
        $GLOBALS['oauth_scopes'] = Array(

        $restRequest = new HttpRestRequest($gbl, $_SERVER);
        $getParams = $restRequest->getQueryParams();

        $response = HttpRestRouteHandler::dispatch(
                $gbl::$FHIR_ROUTE_MAP, $restRequest, 'direct');
        $entries = $response->entry;

        foreach ($entries as $participant) {