Services

Status: [Designed] -- Area 2 of the Grove language. Syntax and semantics are defined; implementation is in progress.

Services declare contracts for external APIs. They define operations (endpoints), their inputs and outputs, and how authentication is handled. Activities reference service operations through the fetch data operation, making external calls type-safe and centrally managed.

Note: This page covers authenticating outbound calls from Grove activities to third-party APIs. For inbound user authentication (who is making a request to your Grove app), see Authentication Overview.

Prerequisites: Activities, Concepts Overview. What you'll learn: How to declare services, define operations, configure credentials and authentication, and reference services from activities.

Declaring a Service

A service is a top-level declaration that groups related operations for an external API:

service stripe {
  credentials {
    api_key: String
  }

  auth {
    protocol: "bearer"
    token: credentials.api_key
  }

  operation create_charge {
    input: {
      amount: Decimal
      currency: String
      customer_id: String
    }
    output: {
      id: String
      status: String
      amount: Decimal
    }
  }

  operation get_charge {
    input: {
      charge_id: String
    }
    output: {
      id: String
      status: String
      amount: Decimal
      refunded: Bool
    }
  }
}

Credentials

The credentials block declares what secrets the service needs at runtime. These are provided through environment configuration, not hardcoded in source:

credentials {
  api_key: String
  webhook_secret: String
}

Credential values are resolved at deployment time from the runtime's secret store. The Grove source only declares their names and types -- it never contains actual secret values.

Authentication

The auth block specifies how credentials are applied to outgoing requests:

auth {
  protocol: "bearer"
  token: credentials.api_key
}

Supported Protocols

ProtocolDescriptionFields
"bearer"Bearer token in the Authorization headertoken
"basic"HTTP Basic authenticationusername, password
"api_key"API key in a header or query parameterkey, header or query
"oauth2"OAuth 2.0 client credentials flowclient_id, client_secret, token_url, scope

Bearer Token

auth {
  protocol: "bearer"
  token: credentials.api_key
}

Basic Authentication

auth {
  protocol: "basic"
  username: credentials.username
  password: credentials.password
}

API Key

auth {
  protocol: "api_key"
  key: credentials.api_key
  header: "X-API-Key"
}

OAuth 2.0

auth {
  protocol: "oauth2"
  client_id: credentials.client_id
  client_secret: credentials.client_secret
  token_url: "https://auth.example.com/token"
  scope: "read write"
}

Operations

Each operation declares the shape of a request and its response:

operation create_customer {
  input: {
    email: String
    name: String
    metadata: Map<String, String>?
  }
  output: {
    id: String
    email: String
    created_at: DateTime
  }
}

Operations are referenced from activity fetch blocks using dot notation: service_name.operation_name.

Input and Output Types

Operation inputs and outputs can use:

Referencing Services from Activities

Activities call service operations through the fetch data operation:

activity create_customer v1 {
  data customer {
    fetch crm.create_customer {
      email: input.email
      name: input.name
    }
  }

  apply(input: CustomerInput) -> CustomerResult {
    return {
      customer_id: customer.id
      created_at: customer.created_at
    }
  }
}

The crm.create_customer reference resolves to the create_customer operation on the crm service. The compiler checks that the fields in the fetch block match the operation's declared input type, and the data block result conforms to the operation's output type.

Versioning

Services can optionally carry version tags:

service stripe v2 {
  // updated operations
}

When a service evolves (new fields, changed response shapes), you create a new version. Activities referencing the old version continue to work until explicitly migrated.

Multiple Services in a Module

A module can declare multiple services. Each represents a distinct external system:

service stripe {
  // payment operations
}

service sendgrid {
  // email operations
}

service twilio {
  // SMS operations
}

Activities within the module can reference any of these services.

Design Notes

Services are contracts, not implementations. The Grove source describes what data goes in and comes out. The runtime adapter maps these contracts to actual HTTP calls, gRPC invocations, or SDK methods. This separation means:

See Also