Class AuthenticatedClient

java.lang.Object
com.qtsurfer.api.sdk.auth.AuthenticatedClient

public final class AuthenticatedClient extends Object
Authenticated SDK session.

Created by QTSurfer.authenticate(String) (or the overload that accepts an AuthOptions). Wraps the underlying api-client, owns a JWT (in memory by default, or in the provided TokenStore), and transparently re-exchanges the API key for a fresh JWT when a call returns 401.

Exposes the same workflow surface as QTSurfer: compile, validateStrategy, strategyState, listStrategies, deleteStrategy, getStrategyCode, dataset management, backtest, backtestResult, sweep, exchanges, instruments, tickers, klines. Method semantics are unchanged — only the bearer token management differs.

Refresh policy: every call first checks the cached token's known expires_in window and proactively re-exchanges it (same POST /v1/auth/token call) a short margin before it would expire — a session left idle past an hour mints a new token on the next call instead of sending one already stale. That covers TTL expiry, but not a token invalidated some other way; for that, a 401 from any call routed through the generated api-client (prepare, execute, result polling and standalone result reads, strategy validation and lookup, exchanges, instruments, tickers, klines) and compile (which talks to its endpoint directly but carries the same ApiException cause on a 401) triggers one more POST /v1/auth/token exchange, then the original call is retried once; a second 401 is surfaced to the caller.

  • Method Details

    • options

      public AuthOptions options()
      Configuration in use by this session.
    • token

      public com.qtsurfer.api.client.model.AuthTokenResponse token()
      Most recently minted token, or null if no exchange has happened yet.
    • refresh

      public com.qtsurfer.api.client.model.AuthTokenResponse refresh()
      Force a fresh JWT exchange via POST /v1/auth/token. Bypasses the cache; the returned token is also written to the configured TokenStore.
    • ensureToken

      public com.qtsurfer.api.client.model.AuthTokenResponse ensureToken()
      Return the cached token, seeding from the TokenStore on first use, minting a new one if neither cache nor store hold one, and proactively re-minting one this session already minted once its expires_in window (minus REFRESH_SKEW) has elapsed — so a session idle past that window mints on the next call instead of sending a token the platform will reject.
    • clear

      public void clear()
      Drop the cached token (in memory and in the store).
    • compile

      public CompletableFuture<Strategy> compile(String source)
    • compile

      public CompletableFuture<Strategy> compile(String source, BacktestOptions opts)
      Compile strategy source into a reusable Strategy handle.

      Issues a single synchronous HTTP request; the compile endpoint returns the strategyId directly, so a failing compile surfaces immediately as QTSStrategyCompileError rather than on a later poll. A 429 means the platform was holding too many compilations and the source was never judged, so it is safe to retry; any other error status reflects a judgment on the submitted code and will not succeed by retrying alone. Only opts.onProgress() is used; polling and timeout settings do not apply to this stage.

      Participates in the session's refresh-on-401 policy the same as every other call — both the proactive TTL check before the request is sent and one retry after a reactive refresh if the platform still returns 401 (the compile endpoint is called directly rather than through the generated client, but its 401 carries the same ApiException cause so this session recognizes it). One behavior this session normally guarantees does not apply here: token resolution happens synchronously before the request is sent — on a session with no cached or stored token, this call blocks to mint one and throws QTSAuthError directly rather than through the returned future.

    • compile

      public CompletableFuture<Strategy> compile(BacktestRequest request)
      Convenience overload that compiles BacktestRequest.strategy() with BacktestOptions.defaults(), ignoring every other field of the request. See compile(String, BacktestOptions).
    • compile

      public CompletableFuture<Strategy> compile(BacktestRequest request, BacktestOptions opts)
      Convenience overload that compiles BacktestRequest.strategy() with the given options, ignoring every other field of the request. See compile(String, BacktestOptions).
    • validateStrategy

      public ValidationOutcome validateStrategy(String strategyId)
      Ask the platform to check that a registered strategy can actually run. The compiled class is instantiated and driven through a bounded synthetic series, so a wiring fault surfaces here instead of at the first backtest. Synchronous — blocks the calling thread for the HTTP round trip. Participates in the session's refresh-on-401 policy: an unauthorized response triggers one token refresh and one retry of this call. Because the call is idempotent, that retry queues nothing extra.

      Did this call start work, and is there a verdict? Two questions, two answers. The call is idempotent: it either queues a check, or queues nothing because the current compilation is already accounted for. The returned ValidationOutcome says which — ValidationOutcome.Queued for the first, ValidationOutcome.NotQueued for the second.

      NotQueued does not mean a verdict exists. The StrategyState it carries can itself be pending — a check queued by an earlier call, possibly from another process, that has not answered yet. So a caller that wants a verdict has to read one either way:

      Polling needs its own deadline. A queued check can go unreported for far longer than one takes — the platform reports that as StrategyState.getValidationStalled() — so pending is not guaranteed to resolve, and a caller that waits without a timeout can wait indefinitely. A stall disproves nothing about the strategy; the check simply has not run. This SDK ships no polling helper.

      passed does not mean the strategy is correct. It means the class loaded and survived the first event of a short synthetic run — a floor, not a guarantee, and not a statement that the strategy is safe to run. When StrategyState.getDryRunIncomplete() is true the check did not even finish its budget, so the floor is lower still and an empty StrategyState.getNotices() list is not a clean bill of health.

      Parameters:
      strategyId - id of a registered strategy, as returned by compile(String)
      Returns:
      whether this call queued a check, and — when it did not — the state the platform holds
      Throws:
      QTSError - on HTTP 4xx/5xx (including 404 when no such strategy is registered for this caller) or transport failure
    • getStrategyState

      public com.qtsurfer.api.client.model.StrategyState getStrategyState(String strategyId)
      Fetch what the platform knows about a registered strategy: that it compiled, what market data the compiled class needs, and what validating it found. Synchronous — blocks the calling thread for the HTTP round trip. Participates in the session's refresh-on-401 policy: an unauthorized response triggers one token refresh and one retry of this call.

      Resolves to the api-client's StrategyState record — the platform's view of the strategy — not to the SDK's Strategy handle that compile(String) produces.

      This is the endpoint to poll while a check is outstanding, whether validateStrategy(String) queued it or reported that one was already accounted for. See that method for what a verdict does and does not mean, and for why a polling loop needs its own deadline.

      A verdict describes the bytecode that produced it, and recompiling supersedes it: when StrategyState.getCompiledAt() is later than StrategyState.getValidatedAt(), the recorded verdict was reached against a compilation that is no longer what would run, and validateStrategy(String) can be called again to refresh it.

      A 404 means exactly one thing — no such registered strategy for this caller. It is never a stale or expired answer; registration and verdict are stored durably, not cached.

      Parameters:
      strategyId - id of a registered strategy, as returned by compile(String)
      Returns:
      the platform's record of the strategy
      Throws:
      QTSError - on HTTP 4xx/5xx or transport failure
    • strategyState

      @Deprecated(forRemoval=false) public com.qtsurfer.api.client.model.StrategyState strategyState(String strategyId)
      Deprecated.
    • getStrategies

      public List<com.qtsurfer.api.client.model.StrategySummary> getStrategies()
      List every strategy registered under this account and not since deleted, most recently compiled first. Synchronous — blocks the calling thread for the HTTP round trip. Participates in the session's refresh-on-401 policy: an unauthorized response triggers one token refresh and one retry of this call.

      Deliberately cheaper than reading each strategy individually: each entry carries the same compiledAt / requiredSources provenance strategyState(String) does, but not validation state, so listing stays cheap no matter how many strategies exist. Check a specific strategy's validation with strategyState(String).

      Never fails with a 404 — an empty list means the caller has no registered strategies, not that the resource is missing.

      Returns:
      the caller's registered strategies
      Throws:
      QTSError - on HTTP 4xx/5xx or transport failure
    • listStrategies

      @Deprecated(forRemoval=false) public List<com.qtsurfer.api.client.model.StrategySummary> listStrategies()
      Deprecated.
    • deleteStrategy

      public void deleteStrategy(String strategyId)
      Release a registered strategy: removes it from both strategyState(String) and listStrategies(). Synchronous — blocks the calling thread for the HTTP round trip. Participates in the session's refresh-on-401 policy: an unauthorized response triggers one token refresh and one retry of this call.

      Not undone by recompiling the same source. Submitting identical source to compile(String) afterward registers a brand-new strategy with a brand-new id — it does not "undelete" this one.

      History is untouched. Backtests already run against this strategy are completely unaffected by deleting it. Deletion only stops the strategy counting against the account and stops future validation or re-run under this id.

      Scoped to the caller's own registration. If this id was copied from someone else's strategy (a shared/marketplace listing), deleting it here never affects their copy, or anyone else's copy of the same source.

      Parameters:
      strategyId - id of a registered strategy, as returned by compile(String)
      Throws:
      QTSError - on HTTP 4xx/5xx (including 404 when no such strategy is registered for this caller) or transport failure
    • getStrategyCode

      public String getStrategyCode(String strategyId)
      Fetch the exact source last submitted for a registered strategy id — the same text compile(String) derived strategyId from, whitespace and comments included. Synchronous — blocks the calling thread for the HTTP round trip. Participates in the session's refresh-on-401 policy: an unauthorized response triggers one token refresh and one retry of this call.

      A 404 here covers two different situations, and deliberately does not distinguish them: the id was never registered by this caller, or it resolves only through a shared/marketplace reference that carries no source of its own. Both mean the same thing from this call's point of view — nothing to return — so both raise the same way.

      Parameters:
      strategyId - id of a registered strategy, as returned by compile(String)
      Returns:
      the raw strategy source last registered for this id
      Throws:
      QTSError - on HTTP 4xx/5xx (including the 404 above) or transport failure
    • executeBacktest

      public CompletableFuture<com.qtsurfer.api.client.model.ResultMap> executeBacktest(BacktestRequest request)
    • executeBacktest

      public CompletableFuture<com.qtsurfer.api.client.model.ResultMap> executeBacktest(BacktestRequest request, BacktestOptions opts)
      Run the full compile → prepare → execute pipeline and resolve once the run reaches a terminal state (completed, failed, or canceled). The returned future completes exceptionally with QTSStrategyCompileError if compilation fails, QTSPreparationError if data preparation fails, QTSExecutionError if execution fails, or QTSTimeoutError if a stage exceeds its configured timeout.

      A 401 at any stage — including compile, see compile(String, BacktestOptions) — triggers one token refresh and then restarts the entire pipeline from compile, not just the stage that failed.

      This call resolves the session's token synchronously before scheduling any async work: on a session with no cached or stored token, it blocks to mint one and throws QTSAuthError directly (not through the returned future) if that mint fails.

      Parameters:
      opts - tuning knobs (poll interval, timeout, progress callback) applied to every stage of the pipeline
    • backtest

      @Deprecated(forRemoval=false) public CompletableFuture<com.qtsurfer.api.client.model.ResultMap> backtest(BacktestRequest request)
    • backtest

      @Deprecated(forRemoval=false) public CompletableFuture<com.qtsurfer.api.client.model.ResultMap> backtest(BacktestRequest request, BacktestOptions opts)
    • getBacktestResult

      public BacktestOutcome getBacktestResult(String exchangeId, String jobId)
      Read what the platform holds for a backtest run, addressed by the exchange it ran on and the id of its execute job. Synchronous — blocks the calling thread for the HTTP round trip. Participates in the session's refresh-on-401 policy: an unauthorized response triggers one token refresh and one retry of this call. The call only reads, so that retry starts nothing extra.

      The run does not have to be one this session started. Every other route to a run's numbers in this SDK goes through the handle backtest(BacktestRequest) or Strategy.backtest(BacktestRequest) hands back, and that handle only exists in the process that submitted the run. A job id that arrived from anywhere else — another client, another session, this one before a restart — has no handle behind it, and this is how to ask the platform about it directly. It compiles nothing, prepares nothing, submits nothing, and starts no second run.

      A run that ended badly is an answer, not a failure of this call. The BacktestOutcome handed back says which of four things the platform is reporting — the run finished, it failed, it was cancelled, or it is still going and has nothing final to say yet. Only a job the platform does not recognise for this caller raises, as a 404. That is a deliberate departure from Backtest.await(), which completes exceptionally on a failed or aborted run: a caller waiting for a result it asked for is not getting one, whereas a caller asking what happened to a job is.

      Waiting for a run to finish remains Backtest.await()'s job, on the process that started it. This does not poll — it is a snapshot, and BacktestOutcome.InProgress means ask again later.

      exchangeId is required and cannot be guessed. A run's result is addressed under the exchange it was submitted against, so a job id on its own does not identify the resource and there is nothing sensible to default the exchange to. An id carried to the wrong exchange does not name the same run.

      Parameters:
      exchangeId - exchange the run was submitted against (e.g. "binance")
      jobId - id of the execute job, as carried by Backtest.id() on the process that submitted it
      Throws:
      QTSError - on HTTP 4xx/5xx (including the 404 above) or transport failure
    • backtestResult

      @Deprecated(forRemoval=false) public BacktestOutcome backtestResult(String exchangeId, String jobId)
    • sweep

      public CompletableFuture<Sweep> sweep(SweepRequest request)
      Parameters:
      request - the grid, the instrument, and the window
      Returns:
      the handle, once the platform has accepted the sweep
    • sweep

      public CompletableFuture<Sweep> sweep(SweepRequest request, SweepOptions opts)
      Run the full compile → prepare → executeSweep pipeline and resolve once the platform has accepted the sweep, handing back a Sweep that keeps polling the leaderboard in the background. See QTSurfer.sweep(SweepRequest, SweepOptions) for why the sweep is one call rather than composable stages, and Sweep.await() for how to read what it found.

      A 401 during compile (see compile(String, BacktestOptions)), prepare, or submission triggers one token refresh and then restarts the entire pipeline from compile — not just the stage that failed. One limit is worth knowing: this does not cover the background leaderboard poll, which starts after this future has already resolved and surfaces on Sweep.await() instead. The handle-scoped Sweep.sensitivity() and Sweep.cancel() sit outside the policy for the same reason.

      This call resolves the session's token synchronously before scheduling any async work: on a session with no cached or stored token, it blocks to mint one and throws QTSAuthError directly (not through the returned future) if that mint fails.

      Parameters:
      request - the grid, the instrument, and the window
      opts - tuning knobs (poll interval, timeout, progress callback, leaderboard ordering) applied to every stage of the pipeline
      Returns:
      the handle, once the platform has accepted the sweep
    • getSweepRunEquityCurve

      public com.qtsurfer.api.client.model.EquityCurveResult getSweepRunEquityCurve(String exchangeId, String requestId, String sweepId, int runIx, com.qtsurfer.api.client.model.EquityCurveOutMode outMode, Integer resample, Boolean differential)
      Read a retained sweep trial's equity curve with one refresh-on-401 retry.

      Pass null for a transform argument to inherit that sweep's submission default. The returned meta says whether points use ARRAY or SHORT output; do not infer that from the requested mode.

      Parameters:
      exchangeId - exchange that owns the sweep
      requestId - prepared-dataset identifier from Sweep.requestId()
      sweepId - sweep identifier from Sweep.id()
      runIx - trial index to read
      outMode - requested point representation, or null for the sweep default
      resample - maximum point count, or null for the sweep default
      differential - whether to delta-encode points, or null for the sweep default
      Returns:
      the retained curve with authoritative response metadata
      Throws:
      QTSError - if the sweep/trial is unknown, its curve was not retained, or the request fails
    • getBoundedSweepRunEquityCurve

      public BoundedEquityCurve getBoundedSweepRunEquityCurve(String exchangeId, String requestId, String sweepId, int runIx, Integer maxResample)
      Read a retained sweep curve as normalized absolute points with a bounded server request.
    • createDataset

      public com.qtsurfer.api.client.model.DatasetCreated createDataset(com.qtsurfer.api.client.model.CreateDatasetRequest request)
      Create a dataset and its first presigned upload session.
    • importDataset

      public com.qtsurfer.api.client.model.DatasetImportCreated importDataset(com.qtsurfer.api.client.model.DatasetImportRequest request)
      Start an external-history import into a new dataset.
    • getDatasetImport

      public com.qtsurfer.api.client.model.DatasetImportState getDatasetImport(String datasetId, String importId)
      Read the fetch and ingest state for one external-history import.
    • getDatasets

      public List<com.qtsurfer.api.client.model.Dataset> getDatasets()
      List the caller's non-deleted datasets, newest first.
    • listDatasets

      @Deprecated(forRemoval=false) public List<com.qtsurfer.api.client.model.Dataset> listDatasets()
      Deprecated.
    • getDataset

      public com.qtsurfer.api.client.model.DatasetWithLinks getDataset(String datasetId)
      Read one dataset and its self link.
    • dataset

      @Deprecated(forRemoval=false) public com.qtsurfer.api.client.model.DatasetWithLinks dataset(String datasetId)
      Deprecated.
    • deleteDataset

      public void deleteDataset(String datasetId)
      Soft-delete a dataset. Existing runs against it are unaffected.
    • finalizeDatasetUpload

      public com.qtsurfer.api.client.model.FinalizeDatasetUpload202Response finalizeDatasetUpload(String datasetId, String uploadId)
      Mark a completed presigned upload ready for ingestion and return its ingest job id.
    • getDatasetUpload

      public com.qtsurfer.api.client.model.DatasetUploadState getDatasetUpload(String datasetId, String uploadId)
      Read an upload's ingest state after it has been finalized.
    • datasetUpload

      @Deprecated(forRemoval=false) public com.qtsurfer.api.client.model.DatasetUploadState datasetUpload(String datasetId, String uploadId)
    • openDatasetUpload

      public com.qtsurfer.api.client.model.DatasetUploadSession openDatasetUpload(String datasetId)
      Open or recover the pending upload session for an existing dataset.
      Parameters:
      datasetId - dataset that will receive the next version
      Returns:
      presigned upload session to pass to uploadDatasetFile(DatasetUploadSession, Path)
      Throws:
      QTSError - on HTTP 4xx/5xx or transport failure
    • getAccount

      public com.qtsurfer.api.client.model.Account getAccount()
      Read the authenticated account's tier and limits.
    • getAccountUsage

      public com.qtsurfer.api.client.model.AccountUsage getAccountUsage()
      Read current account storage consumption, including retained signals.
    • startLive

      public com.qtsurfer.api.client.model.LiveRun startLive(String strategyId, com.qtsurfer.api.client.model.StartLiveRequest request)
      Start a compiled strategy's live run.
    • getLive

      public com.qtsurfer.api.client.model.LiveRun getLive(String strategyId)
      Read a strategy's active or most recent live run.
    • stopLive

      public com.qtsurfer.api.client.model.LiveRun stopLive(String strategyId)
      Request that a strategy's live run stop.
    • listLive

      public com.qtsurfer.api.client.model.LiveListResponse listLive(String cursor, Integer limit)
      List the authenticated account's live runs, newest first.
    • listPublicLive

      public com.qtsurfer.api.client.model.PublicLiveListResponse listPublicLive(String cursor, Integer limit)
      List public live runs, newest first.
    • updateLive

      public com.qtsurfer.api.client.model.LiveRunCompact updateLive(String runId, com.qtsurfer.api.client.model.UpdateLiveRequest request)
      Update mutable metadata for a live run.
    • updateLiveParams

      public com.qtsurfer.api.client.model.LiveParamsUpdateResult updateLiveParams(String runId, com.qtsurfer.api.client.model.UpdateLiveParamsRequest request)
      Update live strategy parameters through a typed request.
    • updateLiveParams

      public com.qtsurfer.api.client.model.LiveParamsUpdateResult updateLiveParams(String runId, UpdateLiveParamsRequestBuilder request)
      Update live strategy parameters using the SDK's fluent request builder.
    • getLiveSignals

      public com.qtsurfer.api.client.model.LiveSignalPage getLiveSignals(String runId, Long sinceMs, String instrument, String cursor, Integer limit)
      Read one oldest-first page of retained signals.
    • getNextLiveSignals

      public Optional<com.qtsurfer.api.client.model.LiveSignalPage> getNextLiveSignals(String runId, com.qtsurfer.api.client.model.LiveSignalPage page)
      Read the next page using its server-provided continuation link.
    • uploadDatasetFile

      public void uploadDatasetFile(com.qtsurfer.api.client.model.DatasetCreated created, Path file)
      Stream a local file to the initial presigned target without attaching API credentials.
      Parameters:
      created - result returned by createDataset(CreateDatasetRequest)
      file - readable regular file to upload
      Throws:
      QTSUploadError - when the transfer fails
    • uploadDatasetFile

      public void uploadDatasetFile(com.qtsurfer.api.client.model.DatasetUploadSession session, Path file)
      Stream a local file to a reopened presigned target without attaching API credentials.
      Parameters:
      session - session returned by openDatasetUpload(String)
      file - readable regular file to upload
      Throws:
      QTSUploadError - when the transfer fails
    • getExchanges

      public List<com.qtsurfer.api.client.model.Exchange> getExchanges()
      List available exchanges on the platform. Synchronous — blocks the calling thread for the HTTP round trip. Participates in the session's refresh-on-401 policy: an unauthorized response triggers one token refresh and one retry of this call.
      Throws:
      QTSError - on HTTP 4xx/5xx or transport failure
    • exchanges

      @Deprecated(forRemoval=false) public List<com.qtsurfer.api.client.model.Exchange> exchanges()
      Deprecated.
    • getInstruments

      public List<com.qtsurfer.api.client.model.InstrumentDetail> getInstruments(String exchangeId)
      List instruments available on the given exchange, including per-data-type coverage and market info. Synchronous — blocks the calling thread for the HTTP round trip. Participates in the session's refresh-on-401 policy: an unauthorized response triggers one token refresh and one retry of this call.
      Parameters:
      exchangeId - exchange identifier (e.g. "binance")
      Throws:
      QTSError - on HTTP 4xx/5xx or transport failure
    • instruments

      @Deprecated(forRemoval=false) public List<com.qtsurfer.api.client.model.InstrumentDetail> instruments(String exchangeId)
      Deprecated.
    • getInstruments

      public List<com.qtsurfer.api.client.model.InstrumentDetail> getInstruments(String exchangeId, String segment)
      List the instruments of one market segment of the given exchange, including per-data-type coverage and market info. Synchronous — blocks the calling thread for the HTTP round trip. Participates in the session's refresh-on-401 policy: an unauthorized response triggers one token refresh and one retry of this call.

      Unwraps the same InstrumentListResponse HAL envelope as instruments(String) and returns just the instrument list. The single-argument overload is the default-segment shortcut and lists the spot segment.

      Parameters:
      exchangeId - exchange identifier (e.g. "binance")
      segment - market segment to list: "spot" or "futures"
      Returns:
      the instruments of that segment
      Throws:
      QTSError - on HTTP 4xx/5xx or transport failure
    • instruments

      @Deprecated(forRemoval=false) public List<com.qtsurfer.api.client.model.InstrumentDetail> instruments(String exchangeId, String segment)
    • downloadTickers

      public InputStream downloadTickers(String exchangeId, String base, String quote, String hour)
    • downloadTickers

      public InputStream downloadTickers(String exchangeId, String base, String quote, String hour, DownloadFormat format)
      Download one hour of raw tickers for an instrument as a streaming InputStream. Synchronous; the caller is responsible for closing the returned stream. Participates in the session's refresh-on-401 policy: an unauthorized response triggers one token refresh and one retry of this call.
      Throws:
      QTSDownloadError - on HTTP 4xx/5xx or transport failure
    • tickers

      @Deprecated(forRemoval=false) public InputStream tickers(String exchangeId, String base, String quote, String hour)
    • tickers

      @Deprecated(forRemoval=false) public InputStream tickers(String exchangeId, String base, String quote, String hour, DownloadFormat format)
    • downloadKlines

      public InputStream downloadKlines(String exchangeId, String base, String quote, String hour)
    • downloadKlines

      public InputStream downloadKlines(String exchangeId, String base, String quote, String hour, DownloadFormat format)
      Download one hour of klines for an instrument as a streaming InputStream. See tickers(String, String, String, String, DownloadFormat) for blocking, closing, and refresh semantics.
      Throws:
      QTSDownloadError - on HTTP 4xx/5xx or transport failure
    • klines

      @Deprecated(forRemoval=false) public InputStream klines(String exchangeId, String base, String quote, String hour)
    • klines

      @Deprecated(forRemoval=false) public InputStream klines(String exchangeId, String base, String quote, String hour, DownloadFormat format)
    • authenticate

      public static AuthenticatedClient authenticate(String apikey, AuthOptions opts)
      Mint a fresh session from the given API key.

      If apikey is null or blank, the value is read from the QTSURFER_APIKEY environment variable. A QTSAuthError is raised when neither source yields a usable API key, or when the initial JWT exchange fails.

    • authenticate

      public static AuthenticatedClient authenticate(String apikey)
    • authenticate

      public static AuthenticatedClient authenticate()
      Equivalent to authenticate(String, AuthOptions) with a null apikey (resolved from QTSURFER_APIKEY) and AuthOptions.defaults().