Explore the directory
Skip to article
Capabilities

Capabilities

An integration is useful when it tells the truth about what it can do. Declare capabilities and preserve the differences that affect your product.

On this page

Model operations explicitly

A single execute() function cannot tell a caller whether a provider returns snippets, full documents, or a generated answer. Name operations according to the output and guarantees they provide, then validate requirements before making a request.

App-owned capability model · illustrative
type SearchCapability =
  | 'web.search'
  | 'web.extract'
  | 'web.answer';

interface ProviderDescriptor {
  id: string;
  capabilities: ReadonlySet<SearchCapability>;
}

function requireCapability(
  provider: ProviderDescriptor,
  capability: SearchCapability,
) {
  if (!provider.capabilities.has(capability)) {
    throw new Error(
      provider.id + ' does not support ' + capability,
    );
  }
}

Capabilities have semantics

CapabilityDocument the details
StreamingWhat is emitted, how errors arrive, and whether cancellation stops upstream work.
PaginationCursor or offset, ordering guarantees, and whether the result set can change between pages.
IdempotencySupported operations, key scope, retention window, and conflicts.
ExtractionSupported formats, partial failures, blocked pages, and content freshness.
TransactionsAtomicity, isolation, connection requirements, and supported database engines.

For a concrete example, Resend's idempotency documentation defines supported operations and a 24-hour key window. Record those limits rather than treating idempotency as an unlimited boolean promise.

Fail honestly

Validate required capabilities during provider selection. Distinguish unsupported behavior from invalid input, permission errors, transient service failures, and partial success. Preserve enough native detail for a maintainer to investigate.

Keep native escape hatches

Your shared boundary should cover common application needs. When a feature only exists in one provider, use that provider's native SDK deliberately. Keep the code clearly named and local to the integration so the dependency is easy to see.

Native SDK option · official Resend API
// Keep provider-specific semantics visible.
await resend.emails.send(
  {
    from: verifiedSender,
    to: [recipient],
    subject: 'Order confirmed',
    text: 'Thank you for your order.',
  },
  { idempotencyKey: 'order-confirmation/' + orderId },
);

This fragment assumes an initialized Resend client and validated application values. The second argument follows the official idempotency key example; reuse the key only for the same logical request.

SDK SDK is an independent discovery hub. Provider examples link to official documentation. App-owned examples are illustrative patterns you can adapt.