> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.morphiaone.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.morphiaone.ai/_mcp/server.

# TypeScript quickstart

This guide uploads one image, submits a Skin Analysis job, and reads its result with
`@morphiaone/sdk`. It runs on Node.js 20 or newer.

> **Info**
>
> **Release status.** API `1.0.0-rc.1` is a release candidate: request and response shapes may still change before the stable release. SDK `0.2.2` is published as a Developer Preview (`@morphiaone/sdk` on npm and `morphiaone` on PyPI).

You need an API key first; [Get access](/getting-started/access) takes you through it.
Use a `pk_test_` key while you build.

## Install the SDK

```bash
npm install --save-exact @morphiaone/sdk@0.2.2
```

To run the TypeScript file directly on Node.js 20, add `tsx` to your project and run
the file with it:

```bash
npm install --save-dev tsx
npx tsx quickstart.mts
```

Keep both values in your server environment. Never put an API key in browser code,
mobile app bundles, source control, or a chatbot client.

```bash
export MORPHIAONE_BASE_URL="https://api.morphiaone.ai"
export MORPHIAONE_API_KEY="pk_test_..."   # the key you created in the Platform
```

## Upload, submit, and wait

Save this as `quickstart.mts` in your server-side Node.js project. Replace `face.jpg`
with an image you are allowed to process.

```ts
import { readFile } from "node:fs/promises";
import { MorphiaOne } from "@morphiaone/sdk";

const { MORPHIAONE_API_KEY: apiKey, MORPHIAONE_BASE_URL: baseUrl } = process.env;
if (!apiKey || !baseUrl) {
  throw new Error("Set MORPHIAONE_API_KEY and MORPHIAONE_BASE_URL");
}

const client = new MorphiaOne({ apiKey, baseUrl });

const source = await client.assets.upload(await readFile("face.jpg"));
const accepted = await client.skin.analyze({
  assetId: source.id,
  concerns: ["acne", "redness"],
  idempotencyKey: "order-48213-skin",
});

const completed = await client.jobs.wait(accepted.id);
if (!completed.output) throw new Error("Completed job has no output");

if (completed.output.known && completed.output.type === "skin.analysis") {
  for (const concern of completed.output.concerns) {
    console.log(concern.concern, concern.status, concern.score);
  }
} else if (!completed.output.known) {
  console.log("Unrecognized output type:", completed.output.type);
}
```

`jobs.wait` polls using the server's `Retry-After` and resolves with a succeeded Job.
If processing fails, it throws `ProcessingError`. A timeout stops local waiting; it does
not cancel work already accepted by the server. Store the Job id if your application
needs to resume later. See [Jobs and outputs](/concepts/jobs).

## Make retries safe

The `idempotencyKey` above comes from the order that owns the work. Keep that key for
every retry of the same logical request. Use a new key for a different request. If you
omit it, the SDK creates a key for that call and reuses it across its own transport
retries, but that key cannot survive an application restart.

See [Idempotency](/concepts/idempotency) for replay behavior and
[Retries and backoff](/concepts/retries) for retryable failures.

## Handle errors by type and code

```ts
import { ApiError, MorphiaOneError, ProcessingError } from "@morphiaone/sdk";

try {
  await client.jobs.wait(accepted.id);
} catch (error) {
  if (error instanceof ProcessingError) {
    console.error("Job failed", error.code, error.jobId);
  } else if (error instanceof ApiError) {
    console.error("Request failed", error.status, error.code, error.requestId);
  } else if (error instanceof MorphiaOneError) {
    console.error("SDK failure", error.code, error.requestId);
  } else {
    throw error;
  }
}
```

Branch on `code`, not the human-readable message. An HTTP success for the submit means
the Job was accepted; a processing failure is reported by the Job and is distinct from
an HTTP error. See [Error handling](/concepts/errors).

## Continue with another product or language

* [Full TypeScript SDK guide](/sdks/typescript) covers Makeup, Hair, Assets, Usage,
  result images, deadlines, and error classes.
* [Python SDK guide](/sdks/python) shows the synchronous and asynchronous clients.
* [REST API onboarding](/rest-api/onboarding) explains each HTTP step.
* [Product availability](/products/availability) lists current product status.