+
My Custom Widget
+
This is a custom front component for Twenty.
+
+ );
+};
+
+export default defineFrontComponent({
+ universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
+ name: 'my-widget',
+ description: 'A custom widget component',
+ component: MyWidget,
+});
+```
+
+Key points:
+- Front components are React components that render in isolated contexts within Twenty.
+- The `component` field references your React component.
+- Components are built and synced automatically during `yarn twenty app:dev`.
+
+You can create new front components in two ways:
+
+- **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new front component.
+- **Manual**: Create a new `.tsx` file and use `defineFrontComponent()`, following the same pattern.
+
+### Skills
+
+Skills define reusable instructions and capabilities that AI agents can use within your workspace. Use `defineSkill()` to define skills with built-in validation:
+
+```typescript
+// src/skills/example-skill.ts
+import { defineSkill } from 'twenty-sdk';
+
+export default defineSkill({
+ universalIdentifier: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
+ name: 'sales-outreach',
+ label: 'Sales Outreach',
+ description: 'Guides the AI agent through a structured sales outreach process',
+ icon: 'IconBrain',
+ content: `You are a sales outreach assistant. When reaching out to a prospect:
+1. Research the company and recent news
+2. Identify the prospect's role and likely pain points
+3. Draft a personalized message referencing specific details
+4. Keep the tone professional but conversational`,
+});
+```
+
+Key points:
+- `name` is a unique identifier string for the skill (kebab-case recommended).
+- `label` is the human-readable display name shown in the UI.
+- `content` contains the skill instructions — this is the text the AI agent uses.
+- `icon` (optional) sets the icon displayed in the UI.
+- `description` (optional) provides additional context about the skill's purpose.
+
+You can create new skills in two ways:
+
+- **Scaffolded**: Run `yarn twenty entity:add` and choose the option to add a new skill.
+- **Manual**: Create a new file and use `defineSkill()`, following the same pattern.
+
+### Generated typed clients
+
+Two typed clients are auto-generated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/generated` based on your workspace schema:
+
+- **`CoreApiClient`** — queries the `/graphql` endpoint for workspace data
+- **`MetadataApiClient`** — queries the `/metadata` endpoint for workspace configuration and file uploads
+
+```typescript
+import { CoreApiClient, MetadataApiClient } from 'twenty-sdk/generated';
+
+const client = new CoreApiClient();
+const { me } = await client.query({ me: { id: true, displayName: true } });
+
+const metadataClient = new MetadataApiClient();
+const { currentWorkspace } = await metadataClient.query({ currentWorkspace: { id: true } });
+```
+
+Both clients are re-generated automatically by `yarn twenty app:dev` whenever your objects or fields change.
+
+#### Runtime credentials in logic functions
+
+When your function runs on Twenty, the platform injects credentials as environment variables before your code executes:
+
+- `TWENTY_API_URL`: Base URL of the Twenty API your app targets.
+- `TWENTY_API_KEY`: Short‑lived key scoped to your application's default function role.
+
+Notes:
+- You do not need to pass URL or API key to the generated client. It reads `TWENTY_API_URL` and `TWENTY_API_KEY` from process.env at runtime.
+- The API key's permissions are determined by the role referenced in your `application-config.ts` via `defaultRoleUniversalIdentifier`. This is the default role used by logic functions of your application.
+- Applications can define roles to follow least‑privilege. Grant only the permissions your functions need, then point `defaultRoleUniversalIdentifier` to that role's universal identifier.
+
+#### Uploading files
+
+The generated `MetadataApiClient` includes an `uploadFile` method for attaching files to file-type fields on your workspace objects. Because standard GraphQL clients do not support multipart file uploads natively, the client provides this dedicated method that implements the [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec) under the hood.
+
+```typescript
+import { MetadataApiClient } from 'twenty-sdk/generated';
+import * as fs from 'fs';
+
+const metadataClient = new MetadataApiClient();
+
+const fileBuffer = fs.readFileSync('./invoice.pdf');
+
+const uploadedFile = await metadataClient.uploadFile(
+ fileBuffer, // file contents as a Buffer
+ 'invoice.pdf', // filename
+ 'application/pdf', // MIME type (defaults to 'application/octet-stream')
+ '58a0a314-d7ea-4865-9850-7fb84e72f30b', // field universal identifier
+);
+
+console.log(uploadedFile);
+// { id: '...', path: '...', size: 12345, createdAt: '...', url: 'https://...' }
+```
+
+The method signature:
+
+```typescript
+uploadFile(
+ fileBuffer: Buffer,
+ filename: string,
+ contentType: string,
+ fieldMetadataUniversalIdentifier: string,
+): Promise<{ id: string; path: string; size: number; createdAt: string; url: string }>
+```
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `fileBuffer` | `Buffer` | The raw file contents |
+| `filename` | `string` | The name of the file (used for storage and display) |
+| `contentType` | `string` | MIME type of the file (defaults to `application/octet-stream` if omitted) |
+| `fieldMetadataUniversalIdentifier` | `string` | The `universalIdentifier` of the file-type field on your object |
+
+Key points:
+- The `uploadFile` method is available on `MetadataApiClient` because the upload mutation is resolved by the `/metadata` endpoint.
+- It uses the field's `universalIdentifier` (not its workspace-specific ID), so your upload code works across any workspace where your app is installed — consistent with how apps reference fields everywhere else.
+- The returned `url` is a signed URL you can use to access the uploaded file.
+
+### Hello World example
+
+Explore a minimal, end-to-end example that demonstrates objects, logic functions, front components, and multiple triggers [here](https://github.com/twentyhq/twenty/tree/main/packages/twenty-apps/hello-world).
diff --git a/packages/twenty-docs/developers/extend/apps/getting-started.mdx b/packages/twenty-docs/developers/extend/apps/getting-started.mdx
new file mode 100644
index 00000000000..c654b5b9b40
--- /dev/null
+++ b/packages/twenty-docs/developers/extend/apps/getting-started.mdx
@@ -0,0 +1,231 @@
+---
+title: Getting Started
+description: Create your first Twenty app in minutes.
+---
+
+