# ctx.http and ctx.auth()

> Reference for ctx.http, the ky HTTP client, authed() credential injection through the Helix proxy, signed S3 URLs, and ctx.auth().

`ctx.http` is a [ky](https://github.com/sindresorhus/ky) instance for outbound HTTP. Call it directly for plain requests, or call `ctx.http.authed(alias)` to route the request through the Helix proxy, which injects credentials server side so secrets never reach your code. For an AWS S3 credential, `authed(alias).s3Sign()` returns a presigned object URL for direct uploads and downloads. `ctx.auth(alias)` returns the non-sensitive variables of a credential record.

## ctx.http: a ky instance

Everything ky supports works on `ctx.http`: `get`, `post`, `put`, `patch`, `delete`, `head`, the `json:` request option, `.json<T>()` on responses, retries, timeouts, `extend()`, and the `beforeRequest`/`afterResponse`/`beforeRetry` hooks.

```typescript
const data = await ctx.http
  .get('https://api.open-meteo.com/v1/forecast', {
    searchParams: { latitude: 51.5, longitude: -0.1 },
    retry: { limit: 3, backoffLimit: 3000 },
    timeout: 10_000,
  })
  .json();
```

Unauthenticated calls go directly to the target but are still logged and traced.

## ctx.http.authed(alias)

```typescript
authed(alias: string): AuthedClient
```

| Parameter | Type | Description |
|---|---|---|
| `alias` | `string` | A key from `authentications` in `helix.config.ts`, mapping to a credential UUID. An unknown alias throws an error listing the available aliases. |

The returned client has one method per HTTP verb: `get`, `post`, `put`, `patch`, `delete`, `head`, and `options`. It is not a ky instance, so ky options such as `searchParams`, `json`, and `retry`, and methods such as `.json()` and `.extend()`, are not available on it. Every request is routed through the Helix proxy, which resolves the credential for that alias, injects it server-side (Bearer for OAuth 2.0, or a named header for API Token), and forwards the call.

Each method takes a full `https:` URL and an optional options object, and returns a promise of `{ status, body, headers }`:

| Option | Type | Description |
|---|---|---|
| `body` | `unknown` | The request body. Strings, byte buffers, blobs, form data, and streams are sent as given. Plain objects and arrays are sent as JSON, with `content-type: application/json` unless you set your own. Bodies over 16 MB are rejected |
| `headers` | `Record<string, string>` | Extra request headers |
| `query` | `Record<string, string \| string[]>` | Query string parameters |
| `timeout` | `number` | Per-call timeout in milliseconds. Defaults to 60,000 |

| Result field | Description |
|---|---|
| `status` | The status the API returned. A 4xx or 5xx is returned, not thrown, and a 3xx is not followed |
| `body` | Parsed JSON for a JSON response, a string for a text response, and raw bytes (`Uint8Array`) for anything else. Undefined when the response is empty |
| `headers` | The response headers |

Use a full target URL in the request. If an OAuth token has expired or the target returns 401, the proxy refreshes the token and retries once. Every request and response is logged with credentials redacted. Credential setup and the auth model are covered in [the connected services guide](/documentation/guides/connected-services/).

## authed(alias).s3Sign(url, options)

For an AWS S3 credential, `s3Sign` returns a presigned S3 URL: a time-limited URL that grants access to one object, with no credentials attached. Hand it to a browser, another service, or your own code, and the transfer goes directly to S3 rather than through your function, which makes it the right tool for large uploads and downloads.

```typescript
s3Sign(url: string, options?: S3SignOptions): Promise<S3SignResult>
```

| Parameter | Type | Description |
|---|---|---|
| `url` | `string` | The full `https:` object URL, either `https://<bucket>.s3.<region>.amazonaws.com/<key>` or `https://s3.<region>.amazonaws.com/<bucket>/<key>`. |
| `options.method` | `'GET' \| 'PUT' \| 'POST' \| 'DELETE' \| 'HEAD'` | The HTTP method the URL is signed for. Defaults to `GET`. The URL only works with this method. |
| `options.expiresIn` | `number` | Seconds until the URL expires. Defaults to 900 (15 minutes); the maximum is 604800 (one week). |
| `options.timeout` | `number` | Per-call timeout in milliseconds. Defaults to 30000. |

The result is `{ url, method, expiresAt }`, where `expiresAt` is the ISO 8601 instant the URL stops working.

```typescript
// functions/api/v1/report-link.get.ts
import { defineFunction } from '@trayai/helix-sdk';

export default defineFunction(async (ctx) => {
  const aws = ctx.http.authed('aws_s3');

  // A download link, valid 15 minutes:
  const download = await aws.s3Sign(
    'https://my-bucket.s3.eu-west-1.amazonaws.com/reports/summary.pdf',
  );

  // An upload link, valid one hour:
  const upload = await aws.s3Sign(
    'https://my-bucket.s3.eu-west-1.amazonaws.com/incoming/video.mp4',
    { method: 'PUT', expiresIn: 3600 },
  );

  return { downloadUrl: download.url, uploadUrl: upload.url, expiresAt: download.expiresAt };
});
```

Signing works with AWS S3 credentials only; an alias for any other service is rejected with a 400 error. The URL is a live grant on the credential until it expires, so treat it like a secret: return it to the caller who needs it, and pick the shortest `expiresIn` that fits the job.

## ctx.auth(alias)

```typescript
auth(alias: string): Promise<Record<string, string>>
```

Returns only the non-sensitive variables of the credential record, for use in logging, conditional logic, or constructing non-HTTP resources. Tokens, API keys, and other secrets are never returned; the platform decides which fields are safe based on the credential record's configuration.

```typescript
const vars = await ctx.auth('salesforce_prod');
// { instance_url: 'https://mycompany.my.salesforce.com', subdomain: 'mycompany' }
```

## Example

```typescript
// functions/api/v1/contacts.get.ts
import { defineFunction } from '@trayai/helix-sdk';

export default defineFunction(async (ctx) => {
  const res = await ctx.http
    .authed('salesforce_prod')
    .get('https://acme.my.salesforce.com/services/data/v59.0/query', {
      query: { q: 'SELECT Id, Name, Email FROM Contact' },
    });

  const result = res.body as {
    records: Array<{ Id: string; Name: string; Email: string }>;
  };

  return result.records;
});
```

---

Canonical: https://helix.tray.ai/documentation/reference/context/http/
Any link on this page is available as markdown by appending .md to its URL.
Full corpus: https://helix.tray.ai/documentation/llms-full.txt