diff --git a/api.bs b/api.bs
index 5f6477a..ee02d7d 100644
--- a/api.bs
+++ b/api.bs
@@ -353,27 +353,43 @@ Upon receiving a set of encrypted histograms from a site, the aggregation servic
 
 # API Details # {#api}
 
-Before using the other Private Attribution APIs, a site must
-[[#list-aggregation-services-api|list aggregation services]] to discover the aggregation services
-that are supported.
-The page may select any of the supported services returned by
-listAggregationServices().
+A site using the Private Attribution API will typically register either
+[=impressions=] or [=conversions=], but in some cases the same site may
+do both.
+
+To register an impression, a site calls
+saveImpression(). No preparation is
+required to use this API beyond collecting parameter values, although
+it may be useful to examine the supported
+aggregationServices in deciding
+whether to use the Private Attribution API.
+
+To request a conversion report, a site calls
+measureConversion().
+Before calling this API, a site must
+select a supported [=aggregation service=].
+The page may select any of the supported services found in
+aggregationServices.
 The name of the selected service must be supplied as
 the `aggregator` member of the
 {{PrivateAttributionConversionOptions}} dictionary when calling the
 measureConversion() method.
 
-## Finding a Supported Aggregation Service ## {#list-aggregation-services-api}
+## Finding a Supported Aggregation Service ## {#find-aggregation-service}
 
 
Is any additional information required in the
 {{PrivateAttributionAggregationService}} dictionary? Do we want
 to rename `apiVersion` to `protocol`? And we should definitely
 define an enum for it.
 
-The listAggregationServices() method
-returns a list of aggregation services supported by the [=user agent=]. The page
+The aggregationServices attribute
+contains a list of aggregation services supported by the [=user agent=]. The page
 must select and specify one of these services when calling the
 measureConversion() method.
+It may also be useful to query the supported services
+before registering an impression,
+but that is not required,
+and impressions are not scoped to a single aggregation service.
 
 
 dictionary PrivateAttributionAggregationService {
@@ -387,7 +403,8 @@ interface PrivateAttribution {
 };
 
 
-The arguments to listAggregationServices() are as follows:
+The aggregationServices attribute
+contains the following information about each supported aggregation service:
 
 
   - name@@ -407,14 +424,14 @@ The arguments to listAggregationServices()
 
 ## Saving Impressions ## {#save-impression-api}
 
-The saveImpression() method requests
+The saveImpression() method requests
 that the [=user agent=] record an [=impression=] in the [=impression store=].
 navigator.privateAttribution.saveImpression({
   histogramIndex: 3,
-  ad: "sample-campaign-eijb",       // a unique identifier for the ad placement
-  conversionSite: "advertiser.example",     // the advertiser site where a conversion will occur
+  ad: "sample-campaign-eijb",
+  conversionSite: "advertiser.example",
 });
 
 
@@ -441,6 +458,20 @@ The arguments to saveImpression() are as fo
     [=impression=] with a subsequent [=conversion=], the [=conversion value=]
     will be added to the histogram bucket identified by this index.
   
+  - ad+
- 
+    A unique ad identifier for the impression. The
+    ad identifier is used to identify which impressions may receive attribution
+    from a [=conversion=]. The Private Attribution API does not impose a particular
+    format on ad identifiers. If an implementation imposes a maximum ad identifer
+    length, it must be at least 64 code points.
+  +
- conversionSite+
- 
+    The site where [=conversions=] for this impression may occur, identified by
+    its domain name. The measureConversion()
+    method will only attribute to this impression when called by the indicated
+    site.
   
- lifetimeDays
- 
     A "time to live" (in days) after which the [=impression=] can no longer
@@ -452,11 +483,12 @@ The arguments to saveImpression() are as fo
 
 ### Operation ### {#save-impression-api-operation}
 
-1. Validate the page-supplied API inputs
-2. Collect the implict API inputs:
+1. Collect the implicit API inputs:
     1. The current timestamp
     2. The impression site domain
-3. If the private attribution API is enabled, save the impression to the store.
+1. Validate the page-supplied API inputs
+1.  If the private attribution API is enabled, save the impression to the
+    [=impression store=].
 
 
 ## Requesting Attribution for a Conversion ## {#measure-conversion}
@@ -467,7 +499,10 @@ and return a [=conversion report=].
 
 The measureConversion() method
 always returns a conversion report,
-regardless of whether matching [=impression|impression(s)=] were found.
+regardless of whether matching [=impression|impression(s)=] are found.
+If there is no match, or if [[#dp|differential privacy]] disallows
+reporting the attribution, the returned conversion report will not
+contribute to the histogram, i.e., will be uniformly zero.
 
 
 navigator.privateAttribution.measureConversion({
@@ -519,14 +554,24 @@ The arguments to measureConversion() are as
 
   - aggregator
- 
-    A selection from the [=aggregation services=] that can be listed using listAggregationServices().
+    A selection from the [=aggregation services=] that can be found in aggregationServices.
   -
- histogramSize
- epsilon+
- The amount of [=privacy budget=] to expend on this [=conversion report=].+
- histogramSize+
- The number of histogram buckets to use in the [=conversion report=].
- logic+
- 
+    A selection from PrivateAttributionLogic indicating the
+    [=attribution logic=] to use.
+  
- value-
- The [=conversion value=]+
- 
+    The [=conversion value=]. If an attribution is made and [[#dp|privacy]]
+    restrictions are satisfied, this value will be encoded into the [=conversion
+    report=].
+  
- lookbackDays
- An integer number of days. Only impressions occurring within the past `lookbackDays` may match this [=conversion=].
- ads@@ -538,21 +583,18 @@ The arguments to measureConversion() are as
 
 ### Operation ### {#measure-conversion-api-operation}
 
-1. Validate the page-supplied API inputs
-2. Collect the implicit API inputs
+1. Collect the implicit API inputs
     1. The current timestamp
     2. The conversion site domain
-3. Set |reportedConversionValue| to 0.
-4. If the private attribution API is enabled, search for a matching impression using the [[#logic-matching|common matching logic]].
-5. If a matching impression was found:
-    1. Set |histogramIndex| to the value from the matching impression
-    2. Set |reportedConversionValue| to the smaller of the following:
-        1.  The conversion value passed to the MeasureConversion API.
-        2.  The limit on conversion value determined by the remaining privacy budget.
-6. Update the privacy budget store to reflect the reported conversion value.
-7. Construct a report from |reportedConversionValue|, |histogramIndex|, and histogramSize.
-8. Encrypt the report.
-9. Return the encrypted report.
+1. Validate the page-supplied API inputs
+    1.  If logic
+        is specified, and the value is anything other than
+        "last-touch",
+        return an error.
+1.  If the private attribution API is enabled,
+    invoke the routine to [=fill a histogram using last-touch attribution=].
+1. Encrypt the report.
+1. Return the encrypted report.
 
 
 ## Impression store ## {#s-impression-store}
@@ -627,7 +669,7 @@ That is, once a week has a matching impression
 and insufficient budget,
 the process will set a value of zero for all histogram buckets.
 
-To fill a histogram using last-touch attribution,
+To fill a histogram using last-touch attribution,
 given |options|:
 
 1.  Initialize |impression| to a null value.
@@ -659,7 +701,7 @@ given |options|:
 
 1.  If |budgetOk| is false, set |value| to 0.
 
-1.  If |impression|.|histogramIndex|
+1.  If |impression|.histogramIndex
     is |options|.{{PrivateAttributionConversionOptions/histogramSize}} or greater,
     set |value| to 0.
 
@@ -679,8 +721,6 @@ TODO specify how to match using "lookbackDays", "ads" and "impressionSites".
 
 Discuss "infinite" lookbackDays. Clarify when it apples. When field is missing? Zero?
 
-ad identifier
-
 To perform common matching logic,
 given |options|, |week|, and [=moment=] |now|: