openapi: 3.2.0 info: title: Multi-Location Analytics Analytics Service API version: 1.16.1 servers: - description: Production url: https://prod.apigateway.co/grpc tags: - name: AnalyticsService paths: /v1/multi-location-analytics/available-metrics: post: description: "The catalog is filtered by audience tier derived from the resource shape:\n - BusinessResourceId or JWTResourceId -> SMB-tier only\n - GroupResourceId, MarketId, or SalesTeam -> SMB + Partner tiers\n\nMixing Business with Market/Group/SalesTeam in one request returns InvalidArgument." operationId: AnalyticsService_GetAvailableMetrics requestBody: content: application/json: schema: $ref: '#/components/schemas/v1GetAvailableMetricsRequest' description: Request to retrieve the catalog of available analytics metrics for the given resources. required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/v1GetAvailableMetricsResponse' description: A successful response. default: content: application/json: schema: $ref: '#/components/schemas/rpcStatus' description: An unexpected error response. security: - OAuth2: - business-app summary: Get Available Metrics tags: - AnalyticsService /v1/multi-location-analytics/query: post: description: "This is the primary analytics endpoint. It supports flexible querying of any available metric\nwith full control over measures, dimensions, time alignment, filtering, grouping, and pagination.\n\nIMPORTANT: Call GetAvailableMetrics first to discover valid metric names and their schemas.\n\n## Quick start\n\n1. Call GetAvailableMetrics to get the list of metric names, measures, and dimensions.\n2. Pick a metric_name (e.g., \"reviews\", \"activation_counts\").\n3. Set partner_id and at least one resource_id (typically a business_id).\n4. Set time_range with start and end_exclusive timestamps.\n5. Set alignment (usually ALIGN_DELTA) and alignment_period (e.g., CALENDAR_MONTH).\n\n## Example: Monthly review counts for a business\n\n{\n \"partner_id\": \"PARTNER-ID\",\n \"metric_name\": \"reviews\",\n \"resource_ids\": [{\"business_id\": {\"business_id\": \"AG-XXXXXXXX\"}}],\n \"time_range\": {\"start\": \"2025-01-01T00:00:00Z\", \"end_exclusive\": \"2026-01-01T00:00:00Z\"},\n \"alignment\": \"ALIGN_DELTA\",\n \"alignment_period\": {\"calendar\": \"CALENDAR_MONTH\"}\n}" operationId: AnalyticsService_QueryMetrics requestBody: content: application/json: schema: $ref: '#/components/schemas/v1QueryMetricsRequest' description: Request to query analytics metrics for multi-location businesses with flexible measures, dimensions, time alignment, filtering, grouping, and pagination. required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/v1QueryMetricsResponse' description: A successful response. default: content: application/json: schema: $ref: '#/components/schemas/rpcStatus' description: An unexpected error response. security: - OAuth2: - business-app summary: Query Analytics Metrics tags: - AnalyticsService components: schemas: rpcStatus: properties: code: format: int32 type: integer details: items: $ref: '#/components/schemas/protobufAny' type: array message: type: string type: object MeasureAggregate: description: Aggregate applies a SQL aggregation function to a measure field. properties: aggOp: $ref: '#/components/schemas/MeasureAggregateOperator' alias: description: 'Optional. An alias for the aggregated result in the response schema. Useful when you have multiple aggregates on the same measure with different filters.' type: string filter: $ref: '#/components/schemas/v1Filter' measure: description: The measure field to aggregate. Must be one of the metric's available measures. type: string type: object v1CompositeFilter: description: A filter that merges multiple other filters using the given operator. properties: filters: description: 'The list of filters to combine. Must contain at least one filter.' items: $ref: '#/components/schemas/v1Filter' type: array op: $ref: '#/components/schemas/v1CompositeFilterOperator' type: object v1Order: default: ORDER_ASC enum: - ORDER_ASC - ORDER_DESC type: string v1GroupResourceId: description: 'A group of businesses identified by a hierarchical path. Groups allow querying aggregate analytics across multiple business locations.' properties: groupPathNodes: description: The ordered list of group path nodes from root to leaf (e.g., ["root-node", "child-node"]). items: type: string type: array type: object v1JWTResourceId: description: A JWT that encodes a specific user/business combination for authenticated end-user queries. properties: jwt: description: A signed JWT containing partner_id and business_id claims. type: string type: object v1GroupByOperator: default: OPERATOR_GROUP_BY enum: - OPERATOR_GROUP_BY - OPERATOR_ROLLUP - OPERATOR_CUBE title: A Group By Operator type: string v1ResourceId: description: 'ResourceId identifies the scope of a query. Exactly one resource type must be set. Each resource type determines what data is returned and how it is aggregated.' properties: businessId: $ref: '#/components/schemas/v1BusinessResourceId' groupId: $ref: '#/components/schemas/v1GroupResourceId' jwtId: $ref: '#/components/schemas/v1JWTResourceId' marketId: $ref: '#/components/schemas/v1MarketId' salesTeam: $ref: '#/components/schemas/v1SalesTeam' type: object MeasureAggregateOperator: default: AVG description: " - AVG: Calculate the average value.\n - COUNT: Count the number of rows.\n - SUM: Sum all values.\n - MIN: Get the minimum value.\n - MAX: Get the maximum value.\n - COUNT_DISTINCT: Count unique values.\n - ARRAY_AGG: Collect all values into an array." enum: - AVG - COUNT - SUM - MIN - MAX - COUNT_DISTINCT - ARRAY_AGG type: string v1MetricResult: description: A single row of metric data with dimension value, measure values, and optional nested results. properties: dimension: description: 'Output only. The value of the dimension for this row (e.g., a date string, source ID, or rating value). When alignment is used, the outermost dimension is the time bucket start timestamp.' readOnly: true measures: description: 'Output only. The measure values for this row, in the same order as measures_schema.properties. Each value corresponds to the property at the same index in the schema.' items: type: object readOnly: true type: array results: $ref: '#/components/schemas/v1Metrics' title: MetricResult type: object GroupByExcludeCombination: properties: dimensions: items: type: string type: array title: 'ExcludeCombination allows a user using ROLLUP OR CUBE to exclude a combination from the result Example: CUBE (pid, agid) returns the combinations ((pid, agid), (pid), (agid), ()). Providing ExcludeCombination{dimensions=[pid, agid]} would return the combinations ((pid), (agid), ())' type: object vendastatypesProperty: properties: description: title: Description of this property type: string name: title: 'Unique identifier for this property. Must be in snake case format. Example(s): account_group_id, listing_id, company_name' type: string piiTag: $ref: '#/components/schemas/PropertyPIITag' properties: description: 'Can only be specified if the Type supplied is a STRUCT. Is the schema of the structured property.' items: $ref: '#/components/schemas/vendastatypesProperty' type: array repeated: description: Indicates if the field is repeated. type: boolean type: $ref: '#/components/schemas/vendastatypesPropertyType' title: Property defines a single property for a schema type: object v1GroupBy: properties: dimension: items: $ref: '#/components/schemas/GroupByDimension' title: A list of dimensions to group by type: array excludeCombination: items: $ref: '#/components/schemas/GroupByExcludeCombination' title: Supports excluding a list of combinations type: array groupByOperator: $ref: '#/components/schemas/v1GroupByOperator' title: Group By is a reducer for aggregating rows into the given dimensions type: object GroupByDimension: properties: dimension: type: string limitDimension: $ref: '#/components/schemas/GroupByLimit' title: Dimension specifies which column to group by type: object v1CompositeFilterOperator: default: OPERATOR_UNSPECIFIED description: "A composite filter operator.\n\n - OPERATOR_UNSPECIFIED: Unspecified. This value must not be used.\n - AND: The results are required to satisfy each of the combined filters.\n - OR: The results are required to satisfy at least one of the combined filters." enum: - OPERATOR_UNSPECIFIED - AND - OR type: string v1SalesTeam: description: One or more sales team IDs for querying analytics scoped to sales team assignments. properties: salesTeamIds: description: The list of sales team IDs to query. items: type: string type: array type: object v1CustomAlignerConfig: properties: alignmentBucketFilter: $ref: '#/components/schemas/v1Filter' skipFilteringEntitiesToDateRange: title: 'Whether to skip filtering entities by the time column on the table to the provided date_range. Usage: date_range is used for both filtering entities and defining the time period to aggregate over. If true, date_range will only be used to define the aggregation time range (i.e. the last year) If false, date_range will be used for BOTH filtering entities AND the aggregation time range' type: boolean type: object GroupByLimit: properties: dimension: type: string limit: format: int64 type: string order: $ref: '#/components/schemas/v1Order' title: Limit reduces the number of dimensions to the specified amount type: object v1Metrics: description: A list of metric result rows. properties: metrics: items: $ref: '#/components/schemas/v1MetricResult' type: array type: object v1TypedValue: description: A message that can hold any of the supported value types. properties: value: title: the value of the field valueType: $ref: '#/components/schemas/vendastatypesPropertyType' type: object AlignmentPeriodCalendar: default: UNSPECIFIED description: "- UNSPECIFIED: No value has been specified.\n - CALENDAR_MINUTE: Align at minute boundaries\n - CALENDAR_HOUR: Align at hour boundaries\n - CALENDAR_DAY: Align at day boundaries\n - CALENDAR_WEEK: Align at week boundaries (where beginning of the week is monday)\n - CALENDAR_MONTH: Align at month boundaries\n - CALENDAR_QUARTER: Align at quarter boundaries\n - CALENDAR_YEAR: Align at year boundaries" enum: - UNSPECIFIED - CALENDAR_MINUTE - CALENDAR_HOUR - CALENDAR_DAY - CALENDAR_WEEK - CALENDAR_MONTH - CALENDAR_QUARTER - CALENDAR_YEAR type: string v1DateRange: properties: end: format: date-time title: '`end` is implicitly inclusive' type: string endExclusive: format: date-time type: string start: format: date-time title: '`start` is implicitly inclusive' type: string startExclusive: format: date-time type: string title: Specifies a range of time for analytics queries type: object v1AlignmentPeriod: properties: calendar: $ref: '#/components/schemas/AlignmentPeriodCalendar' duration: type: string type: object v1MetricDescription: description: Description of a queryable analytics metric, including its available measures, dimensions, and time field. properties: description: description: Output only. A human-readable description of what this metric measures. readOnly: true type: string dimensions: items: type: string readOnly: true title: 'Output only. The dimension fields available for this metric. These can be used in QueryMetricsRequest.group_by and QueryMetricsRequest.filter. Examples: "source_id", "rating", "account_group_id"' type: array measures: items: type: string readOnly: true title: 'Output only. The measure fields available for this metric. These can be used in QueryMetricsRequest.measures for selection or aggregation. Examples: "scaled_stars", "review_count", "likes"' type: array name: readOnly: true title: 'Output only. The unique identifier for this metric. Pass this as QueryMetricsRequest.metric_name. Examples: "reviews", "activation_counts", "facebook_post_stats"' type: string timeField: description: 'Output only. The primary time field used for date range filtering and time alignment. This is the field that alignment_period and time_range operate on.' readOnly: true type: string title: MetricDescription type: object v1FieldFilter: properties: comparisonField: title: A field to compare the dimension to type: string dimension: description: The field to filter by. type: string operator: $ref: '#/components/schemas/v1FieldFilterOperator' operatorFunction: $ref: '#/components/schemas/FieldFilterOperatorFunction' operatorFunctionOnField: title: Whether to apply the specified operator function to the field, or the value involved in the filter type: boolean value: $ref: '#/components/schemas/v1TypedValue' title: A filter on a specific field type: object OrderByOrderColumn: properties: column: title: Either a dimension or a measure to order by type: string order: $ref: '#/components/schemas/v1Order' type: object v1BusinessResourceId: description: A single business location identified by its account group ID. properties: businessId: description: The account group ID of the business (e.g., "AG-XXXXXXXX"). type: string type: object vendastatypesSchema: properties: properties: items: $ref: '#/components/schemas/vendastatypesProperty' type: array title: Schema describes the structure of data in a storage-agnostic way type: object v1Filter: properties: compositeFilter: $ref: '#/components/schemas/v1CompositeFilter' fieldFilter: $ref: '#/components/schemas/v1FieldFilter' unaryFilter: $ref: '#/components/schemas/v1UnaryFilter' title: A filter type: object v1UnaryFilter: properties: dimension: description: The field to which to apply the operator. type: string op: $ref: '#/components/schemas/v1UnaryFilterOperator' title: A filter with a single operand type: object protobufAny: additionalProperties: {} properties: '@type': type: string type: object v1FieldFilterOperator: default: OPERATOR_UNSPECIFIED description: " - OPERATOR_UNSPECIFIED: Unspecified. This value must not be used.\n - LESS_THAN: Less than. Requires that the field come first in `order_by`.\n - LESS_THAN_OR_EQUAL: Less than or equal. Requires that the field come first in `order_by`.\n - GREATER_THAN: Greater than. Requires that the field come first in `order_by`.\n - GREATER_THAN_OR_EQUAL: Greater than or equal. Requires that the field come first in\n`order_by`.\n - EQUAL: Equal.\n - OVERLAP: Overlap (have elements in common). Field and values must be lists.\n - NOT_EQUAL: Not Equal." enum: - OPERATOR_UNSPECIFIED - LESS_THAN - LESS_THAN_OR_EQUAL - GREATER_THAN - GREATER_THAN_OR_EQUAL - EQUAL - OVERLAP - NOT_EQUAL type: string v1GetAvailableMetricsRequest: description: Request to retrieve the catalog of available analytics metrics for the given resources. properties: partnerId: description: Required. The partner ID. type: string resourceIds: description: Required. At least one resource ID. items: $ref: '#/components/schemas/v1ResourceId' type: array required: - resourceIds - partnerId title: GetAvailableMetricsRequest type: object v1OrderBy: properties: orderBy: items: $ref: '#/components/schemas/OrderByOrderColumn' type: array type: object v1QueryMetricsResponse: description: Response containing analytics metric results for each requested resource. properties: metricResults: description: Output only. Results for each requested resource, in the same order as the request's resource_ids. items: $ref: '#/components/schemas/v1ResourceMetricResult' readOnly: true type: array title: QueryMetricsResponse type: object v1QueryMetricsRequest: description: Request to query analytics metrics for multi-location businesses with flexible measures, dimensions, time alignment, filtering, grouping, and pagination. properties: alignment: $ref: '#/components/schemas/v1Alignment' alignmentPeriod: $ref: '#/components/schemas/v1AlignmentPeriod' cursor: description: 'Optional. Pagination cursor from a previous response''s ResourceMetricResult.cursor. Pass an empty string or omit to start from the beginning.' type: string customAligner: $ref: '#/components/schemas/v1CustomAlignerConfig' dateRange: $ref: '#/components/schemas/v1DateRange' filter: $ref: '#/components/schemas/v1Filter' groupBy: $ref: '#/components/schemas/v1GroupBy' limit: description: 'Optional. Maximum number of result rows to return. 0 means no limit. Use with order_by for "top N" queries (e.g., top 10 businesses by review count).' format: int64 type: string measures: description: 'Optional. The measures to select or aggregate from the metric. Each metric has a set of available measures (returned by GetAvailableMetrics). If omitted, all default measures for the metric are returned. Use Measure.measure for raw selection (e.g., "scaled_stars") or Measure.aggregate for aggregations (e.g., AVG of "scaled_stars").' items: $ref: '#/components/schemas/v1Measure' type: array metricName: title: 'Required. The name of the metric to query. Must be one of the names returned by GetAvailableMetrics. Examples: "reviews", "activation_counts", "facebook_post_stats", "net_promoter_score"' type: string orderBy: $ref: '#/components/schemas/v1OrderBy' partnerId: description: Required. The partner ID to scope the query to. All queries are scoped to a single partner. type: string resourceIds: description: 'Required. At least one resource ID to scope the query to. Multiple resource IDs of the same type can be provided to query across multiple businesses, groups, or markets in a single call.' items: $ref: '#/components/schemas/v1ResourceId' type: array timeRange: $ref: '#/components/schemas/v1DateRange' required: - partnerId - metricName - resourceIds title: QueryMetricsRequest type: object v1Alignment: default: ALIGN_NONE description: " - ALIGN_NONE: No alignment.\n - ALIGN_DELTA: Align and convert to delta metric type. This alignment is valid\nfor delta metrics. Aligning an existing delta metric to a delta metric\nrequires that the alignment period be increased. The value type of the\nresult is the same as the value type of the input.\n\nOne can think of this aligner as a rate but without time units; that\nis, the output is conceptually (second_point - first_point).\n - ALIGN_INTERPOLATE: Align by interpolating between adjacent points around the\nperiod boundary. This alignment is valid for gauge\nmetrics with numeric values. The value type of the result is the same\nas the value type of the input.\n - ALIGN_NEXT_OLDER: Align by shifting the oldest data point before the period\nboundary to the boundary. This alignment is valid for gauge\nmetrics. The value type of the result is the same as the\nvalue type of the input.\n - ALIGN_EXACT: Align by using data points that fall on the same UTC day of the period boundary.\nThis alignment is valid for daily gauge metrics.\nThe value type of the result is the same as the value type of the input.\n - ALIGN_CUSTOM: Allows the user to specify custom alignment logic, allowing the query to make more complex decisions on which\ntime/date bucket(s) a row should be placed in.\nThis is useful when your table has multiple columns that need to be considered when bucketing rows into time\nbuckets, such as a created and deleted date.\nThe custom aligner allows you to bucket these rows based on their status at one of the bucket boundaries or\nwithin the bucket." enum: - ALIGN_NONE - ALIGN_DELTA - ALIGN_INTERPOLATE - ALIGN_NEXT_OLDER - ALIGN_EXACT - ALIGN_CUSTOM type: string PropertyPIITag: default: PROPERTY_PII_TAG_CLEAN description: "PIITag defines potential PII tags.\n\n - PROPERTY_PII_TAG_CLEAN: clean means the data is not PII or PD.\n - PROPERTY_PII_TAG_PII: PII means Personally Identifiable Information. It is information that can be used to retrieve personal data, such as IAM user ID.\n - PROPERTY_PII_TAG_PD: PD means personal data, such as name, email and address. They are sensitive data we need to protect in our system." enum: - PROPERTY_PII_TAG_CLEAN - PROPERTY_PII_TAG_PII - PROPERTY_PII_TAG_PD type: string vendastatypesPropertyType: default: PROPERTY_TYPE_STRING enum: - PROPERTY_TYPE_STRING - PROPERTY_TYPE_INT64 - PROPERTY_TYPE_DOUBLE - PROPERTY_TYPE_BOOL - PROPERTY_TYPE_TIMESTAMP - PROPERTY_TYPE_GEOPOINT - PROPERTY_TYPE_STRUCT - PROPERTY_TYPE_BYTES type: string FieldFilterOperatorFunction: default: NONE description: "- NONE: No function will be applied to the values\n - ANY: Any value in the list must pass the filter\n - ALL: All values in the list must pass the filter" enum: - NONE - ANY - ALL type: string v1ResourceMetricResult: description: Analytics result for a single resource, including data, schemas, and pagination cursor. properties: cursor: description: 'Output only. Pagination cursor. Pass this value as QueryMetricsRequest.cursor to fetch the next page. Empty when there are no more results.' readOnly: true type: string dimensionsSchema: $ref: '#/components/schemas/vendastatypesSchema' explodedMetrics: $ref: '#/components/schemas/v1QueryMetricsResponse' measuresSchema: $ref: '#/components/schemas/vendastatypesSchema' metrics: $ref: '#/components/schemas/v1Metrics' resourceId: $ref: '#/components/schemas/v1ResourceId' title: ResourceMetricResult type: object v1Measure: description: 'Measure selects or aggregates a field from the metric''s available measures. Use GetAvailableMetrics to discover which measure names are valid for each metric. Example: Select a raw measure {"measure": "scaled_stars"} Example: Aggregate a measure {"aggregate": {"measure": "scaled_stars", "agg_op": "AVG"}} Example: Count distinct values with an alias {"aggregate": {"measure": "account_group_id", "agg_op": "COUNT_DISTINCT", "alias": "unique_businesses"}}' properties: aggregate: $ref: '#/components/schemas/MeasureAggregate' measure: title: 'Select a measure field as-is without aggregation. Example: "scaled_stars"' type: string type: object v1GetAvailableMetricsResponse: description: The catalog of available analytics metrics with their schemas. properties: metrics: description: 'Output only. All available metrics with their schemas. Use this to discover valid metric names, measures, and dimensions for building QueryMetricsRequest messages.' items: $ref: '#/components/schemas/v1MetricDescription' readOnly: true type: array title: GetAvailableMetricsResponse type: object v1UnaryFilterOperator: default: OPERATOR_UNSPECIFIED description: "A unary operator.\n\n - OPERATOR_UNSPECIFIED: Unspecified. This value must not be used.\n - IS_NAN: Test if a field is equal to NaN.\n - IS_NULL: Test if an expression evaluates to Null.\n - IS_NOT_NULL: Test if an expression evaluates to Not Null." enum: - OPERATOR_UNSPECIFIED - IS_NAN - IS_NULL - IS_NOT_NULL type: string v1MarketId: description: 'One or more market IDs. Markets are geographic or logical groupings of businesses within a partner. When market_ids is empty, the query returns data across ALL markets for the partner (partner-wide query).' properties: marketIds: description: The list of market IDs to query. An empty list means all markets for the partner. items: type: string type: array type: object securitySchemes: OAuth2: flows: authorizationCode: authorizationUrl: https://sso-api-prod.apigateway.co/oauth2/auth scopes: business-app: Allows the application to manage all features of Business App tokenUrl: https://sso-api-prod.apigateway.co/oauth2/token type: oauth2