Skip to content
Changelog

Report a bug

Call Connected Apps and APIs

Automation code has no fetch. It calls connected apps through Type. Type adds the credentials and checks access on each call. Your code never sees a credential.

You want toUse
Read with an integration tool, with a typed resultctx.read.call(call, schema)
Change data with an integration tool, with a typed resultctx.write.call(call, schema, { effectKey })
Read from a Custom API connectionctx.read.request(request, schema)
Change data in a Custom API connectionctx.write.request(request, schema, { effectKey })
Call a tool that returns only textctx.tools.call(call, options)
Post in the Spacectx.write.sendMessage(message, { effectKey })

Each connected app has tools. To call one, you need its service, name, and input schema. Use one of these:

  • Ask Type in the Space: “Find the Linear tool that lists issues, with its schema.”
  • Call ctx.tools.find({ service: "linear" }) in code during development.
  • Run find_tools with includeSchemas: true in a Type thread.

Write down the exact service, name, and organizationIntegrationId. Use them as constants in your code. The tool names on this page are examples. Use the names that find returns for your connection.

const issues = z.object({
issues: z.array(z.looseObject({ id: z.string(), title: z.string() })),
});
const result = await ctx.read.call(
{
service: "linear",
name: "list_issues",
organizationIntegrationId: "orgint_abc123",
arguments: { first: 20 },
},
issues,
);
if (result.isErr()) return err(result.error);
for (const issue of result.value.issues) {
// issue.id and issue.title are typed
}

ctx.read.call reads the tool’s structuredContent and parses it with your schema.

  • Describe only the fields that you use.
  • Use z.looseObject(), so new fields from the provider do not fail the run.
  • The schema describes the provider data, not the tool envelope.
const result = await ctx.tools.call({
service: "linear",
name: "get_issue",
organizationIntegrationId: "orgint_abc123",
arguments: { id: "ENG-123" },
});
if (result.isErr()) return err(result.error);
if (result.value.isError) return err(new TypeApiError("Linear returned an error", "invalid_response"));
const text = result.value.content.map((item) => item.text ?? "").join("\n");

Check both result.isErr() and result.value.isError. A tool can report an error inside a successful response.

Use ctx.write.call for tools that change data. It needs an effectKey:

const created = await ctx.write.call(
{
service: "linear",
name: "create_issue",
organizationIntegrationId: "orgint_abc123",
arguments: { teamId: "TEAM", title: `Deploy failed: ${event.id}` },
},
z.looseObject({ id: z.string() }),
{ effectKey: `linear-issue:${event.id}` },
);

Make the effect key stable for the same change. Build it from the event ID or from scheduledAt. See Write Safely.

For a Custom API connection, send HTTP requests with ctx.read.request and ctx.write.request:

const orders = await ctx.read.request(
{ organizationIntegrationId: "orgint_shop123", method: "GET", path: "/orders", query: { status: "open" } },
z.object({ orders: z.array(z.looseObject({ id: z.string() })) }),
);
  • path is relative to the connection’s base URL.
  • Type adds the connection’s credentials.
  • The call fails if the response is not successful or is truncated. It also fails if the body does not match the schema.
  • The automation uses the access of the person who created it, and the connections of its Space.
  • An agent in the Space can have more access than the automation. If a call works in a thread, it can still fail in the automation.
  • Type checks access on each call. If someone removes a connection from the Space, later calls fail.
  • 128 requests to Type in each run.
  • 2 MiB for each response. Ask for smaller pages if a response is larger.
  • 1 MiB for each request body.
  • A tool call to the sandbox can take up to 170 seconds.